Jens Gustedt, INRIA and ICube, France
2025年09月07日
integration into IS ISO/IEC 9899:202y
C document ID: C4172
| document number | date | comment |
|---|---|---|
| n3543 | 202504 | original proposal |
| along the lines vote in Brno for integer types 22/0/6 | ||
| n3707 | 202509 | remove floating types |
place in <stdlib.h> |
||
| make the interfaces unsequenced |
Generic features to compute minimum and maximum value of two given values is notoriously challenging in C. Motivating examples for this proposal are the following
constexpr size_t n = SIZE_MAX;
static double A[stdc_max(n, 1)];
enum { small = stdc_min(something, -1), };That is, minimum and maximum features that are usable where a integer
constant expression is needed and that are able to mix comparison of
signed (1 and
-1)
and unsigned (n and perhaps something) without hickup.
The difficulties with such features are as follows
We propose to add type-generic macros that solve all these problems for all integer value and type combinations. Namely:
In particular the choice for the minimum differs from the usual
arithmetic conversions. If one type is signed and the other is unsigned
the macro stdc_min chooses the signed
type for the result.
The proposed features have to be implemented side effect free and
thus, seen as function interfaces, they should be pure in the CS sense
of the term. So, as we have already have done for the bit manipulation
macros, for these new interfaces we add the [[unsequenced]]
attribute.
For convenience, we propose to add the features to the <stdlib.h>
header, 7.25.7 "Integer arithmetic functions", with the idea this header
already contains functions that operate on integers. But, the suggested
wording could easily be adapted to be placed in a different header
(clause 7) or even to be introduced as proper operators (clause 6).
The choice for the name is guided by the fact that min and max identifiers are abundantly used in user
code, not only for functions or macros that perform these operations,
but also as variable names that store the respective minimum or maximum
value in a given situation. The prefix stdc_, which is reserved for identifiers
with external linkage, seemed the most natural one: sufficiently short
and unambiguous.
Implementations of minimum and maximum functions there are plenty. Even the current C standard provides a handful of slightly different interfaces for maximum functions, but only for floating types. Then, in the field an abundant number of implementations of varying properties and quality are added to that picture.
We are not aware of an implementation of these functionalities that
resolves all the problems that are listed in the introduction. In
particular, none of them seems to provide an integer constant expression
where this would be possible. This is all the more surprising as that
property has a clear demand (in particular for array lengths) and as
providing macros that have that property is not too difficult by using
either implementation-specific extensions (such as gcc’s __builtin_constant_p) and/or playing tricks
with _Generic.
The goal of this paper is to propose a unification to all these interfaces and their behavior, such that programmers find reliable implementations of these features in their C library.
The reference implementation that is available on the WG14 git (and
that we are able to provide on request) is not meant to suggest any
particular way in which these features should be implemented, but only
to prove that an implementation is possible as of today with relatively
small effort. Besides a lot of C23-only features, this reference
implementation uses only the __COUNTER__ pseudo macro (which is accepted
for inclusion into C2Y).
We also provide an alternative implementation that uses compound
expressions which could easily be replaced with lambdas or other forms
of function literals. The advantage of that is that it avoids declaring
a whole bunch of auxiliary inline functions. Its disadvantage is that
this implementation with extended expressions currently doesn’t work for
integer constant expressions in file scope. This is due to a
restriction in gcc that forbids compound expressions in file scope, even
if they are only present in a dead branch of a _Generic
expression.
Our expectation is that compilers will provide buildins for these features and that the macros proposed here will then only serve as stubs to ensure standard conformance.
In 7.25, bump the value of __STDC_VERSION_STDLIB_H__.
Then add a new clause
(追記)(追記ここまで) (追記)7.25.7.3 Minimum and maximum operation type-generic macros
(追記ここまで) (追記)1 The following macros perform minimum and maximum operations. These operations are applicable to pairs of values of any integer type. In the following
AandBdenote the promoted type of the first and second argument of an invocation, respectively.
(追記ここまで) (追記)2 Synopsis
#include <stdlib.h>
minType stdc_min(A a, B b) [[unsequenced]];
maxType stdc_max(A a, B b) [[unsequenced]];(追記ここまで)Description
(追記) 3 After argument promotion, these type-generic macros compute the minimum and maximum value of their arguments. If both arguments are integer constant expressions, the macro invocation is an integer constant expressions, too. (追記ここまで)
(追記) 4 The result type(追記)maxTypeis the common real type of the typesAandBafter the usual arithmetic conversions (6.3.2.8). The result typeminTypeis determined as follows: (追記ここまで)
(追記ここまで) (追記)
- If
maxTypeis an unsigned type and if one of the typesAorBis signed,minTypeis that signed type.- Otherwise,
minTypeis the same asmaxType.
(追記ここまで)Returns
(追記) 5 The result of the operation is the value of the mathematically lesser (respectively greater) argument converted to type(追記)minType(respectivelymaxType). (追記ここまで)
(追記ここまで)6 NOTE The types
minTypeandmaxTypeare able to represent the mathematical result of the corresponding operation.
(追記) 7 Example (追記ここまで)(追記)
#include <stdlib.h>
constexpr size_t n = SIZE_MAX;
static double A[stdc_max(n, 1)];
constexpr auto max1 = stdc_max(n, 0); // (size_t)n
constexpr auto min1 = stdc_min(n, 0); // (signed int)0
constexpr auto max2 = stdc_max(n, 0u); // (size_t)n
constexpr auto min2 = stdc_min(n, 0u); // (size_t)0
constexpr auto max3 = stdc_max(n, (char)0); // (size_t)n
constexpr auto min3 = stdc_min(n, (char)0); // (size_t)0 or (signed int)0
constexpr auto max4 = stdc_max(3wb, 7wbu); // (unsigned _BitInt(3))7
constexpr auto min4 = stdc_min(3wb, 7wbu); // (signed _BitInt(3))3
constexpr auto max5 = stdc_max(-1wb, 255wbu); // (unsigned _BitInt(8))255
constexpr auto min5 = stdc_min(-1wb, 255wbu); // (signed _BitInt(2))-1
constexpr auto max6 = stdc_max(-1wbu, 255wbu); // (unsigned _BitInt(8))255
constexpr auto min6 = stdc_min(-1wbu, 255wbu); // (unsigned _BitInt(8))1(追記) Here, the array length expression ofAcomputes the maximum of two integer constant expressions and is thus an integer constant expression, too. Formin1, one of the promoted arguments tostdc_minhas a signed type, so the result type is that signed type. Conversely, formin2both promoted arguments have an unsigned type, and so the result is the common real type. The type ofmin3depends on whether or notcharcan hold negative values and of its width. Ifcharhas no negative values and the same width asintit promotes tounsigned intand thus the result type issize_t; otherwise the result issigned int. The type ofmax4is the common real type ofsigned _BitInt(3)(for3wb) andunsigned _BitInt(3)(for7wbu) which isunsigned _BitInt(3). Formin4with the same arguments, the result type is signed type of the two,signed _BitInt(3). Formin5, since one of the types is signed the result is that typesigned _BitInt(2). Formin6,-1wbuis the maximum value ofunsigned _BitInt(1)which is1; since both of the types are unsigned the result is converted to the common real typeunsigned _BitInt(8). (追記ここまで)