1.1. Revision 1 - June 17th, 2022
Adjust the return values to use negative values for implementation-defined types.
Clarify exactly which types produce what values.
Remove change to
Clarify relationship to Decimal Floating and Complex Floating types in prose and wording.
Add value specifically for "predefined constants" (
1.2. Revision 0 - April 12th, 2022
Initial release! 🎉
2. Introduction and Motivation
Users did not have a way to adequately query support of specific kinds of literals or the maximum integer their processor could handle without triggering constraint violations for handling tokens in the preprocessor. It was only in the upcoming C23 Standard that users received a way to check whether or not an integer constant they wrote was valid by checking the width of that value against
This, however, still leaves other problems out in the open.
There are implementations with special floating point, string, and other kinds of literals built in as extensions to the language. They cannot be adequately captured by the standard and thus leaves users to fall back to using special compiler-specific preprocessor macros to gate usage so as not to produce errors. Worse, the integer problem ties the Application Binary Interface problems of
to all literals made by the implementation, typically limiting them to 64-bit maximum values for their literals. This has becoming an increasingly difficult problem, and many proposals - including one for bit-precise integer types (
, [N2946]) and one for the bit-specific integer types ([N2889]) - are trying to divorce
from certain types so they can be larger than the type.
While these are all good movements in the right direction, we propose a more fundamental fix that can be applied to more cases and allow for easier future growth in the space of literals, while simultaneously improving the situation around integer-based literals related to
. The feature is a
preprocessor directive, which returns a positive integer for recognized constant/literal token sequence, and
is a preprocessor macro function that is usable in
preprocessor directives and produces an integer constant expression. It takes a
s sequence and determines whether or not the given sequence resolves to a literal. The return values are as follows:
, which means the token sequence does not resolve to any kind of literal, or is a recognized literal that exceeds the limitations of the literal type (e.g., a too-large integer literal);
, which means the token sequence resolves to a recognized standard integer literal;
, which means the token sequence resolves to a recognized bit-precise integer literal;
, which means the token sequence resolves to a recognized standard floating point, decimal floating point, or complex literal;
, which means the token sequence resolves to either the
falsepredefined tokens; and,
, which means the token sequence resolves to a recognized character literal; and,
, which means the token sequence resolves to a recognized string literal.
This allows an implementation to properly advertise support for various constructs as provided by their implementation. For example, the GNU Compiler Collection (GCC) supports "raw string literals" (see: [raw-string-literals]), but only with
. Therefore, it can be detected by writing:
#if __supports_literal(R"meow(🐈😸😹😺😻)meow") // Supports the raw string literal syntax #else // Does NOT support raw string literal syntax #endif
A more concrete example of this are types which extend the typical preprocessor syntax beyond just larger integer support or similar. For example, shading languages like HLSL and GLSL have built-in support for providing floating constants as a 16-bit type named "half" with the syntax
is the floating point suffix here. Some implementations simply support 128-bit integers natively in their compilers as well, but often cannot give support for it in the preprocessor due to the limitations of
and the ABI problems which surround upgrading the type. Finally, there are extensions to write decimal floating point types directly as floating point constants as well.
This syntax enables software engineers hoping to write robust C code to check for the support for all of these things and act accordingly, allowing for a consistent way to enable constants of types outside of what is currently blessed by the C Standard and already in use with many existing implementations and direct derivatives of C.
Wording is relative to [N2912].
4.1. Modify 6.10.1 Conditional inclusion with new syntax, a
expression, and new Recommended Practice
6.10.1 Conditional inclusionSyntax…
__has_include ( header-name )
__has_include ( header-name-tokens )
…ConstraintsThe expression that controls conditional inclusion shall be an integer constant expression except that: identifiers (including those lexically identical to keywords) are interpreted as described below182) and it may contain zero or more defined macro expressions, has_include expressions, and/or has_c_attribute expressions as unary operator expressions.…Prior to evaluation, macro invocations in the list of preprocessing tokens that will become the controlling constant expression are replaced (except for those macro names modified by the defined unary operator), just as in normal text. If the token defined is generated as a result of this replacement process or use of the defined unary operator does not match one of the two specified forms prior to macro replacement, the behavior is undefined. After all replacements due to macro expansion and evaluations of defined macro expressions, has_include expressions, and has_c_attribute expressions have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number
, and then each preprocessing token is converted into a token. The resulting tokens compose the controlling constant expression which is evaluated according to the rules of 6.6. For the purposes of this token conversion and evaluation, all signed integer types and all unsigned integer types act as if they have the same representation as, respectively,
0 the types
defined in the header
.183) This includes interpreting character constants, which may involve converting escape sequences into execution character set members. Whether the numeric value for these character constants matches the value obtained when an identical character constant occurs in an expression (other than within a
< stdint . h >
directive) is implementation-defined.
4.2. Add a extra paragraph to H.5.2 Constants in Annex H for
H.5.2 Conditional inclusion…