




Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
A constexpr function, called with arguments that are all themselves constant expressions, is a constant expression. A constexpr function may ...
Typology: Schemes and Mind Maps
1 / 8
This page cannot be seen from the preview
Don't miss anything!





Proposal for C WG14 2851 Title: The constexpr specifier Author, affiliation: Alex Gilding, Perforce Date: 2021-10- Proposal category: New features Target audience: Compiler/tooling developers, library developers, application developers
C++ has supported compile-time evaluation of first-class functions for over ten years, while C is still limited to using second-class language features in compile-time contexts. This puts C at a significant disadvantage in terms of being able to share the same features between runtime and compile-time, and in being able to assert truths about the program at compile time rather than waiting to assert in a runtime debug build. constexpr provides the ability for a C program to call a restricted set of functions at compile time while leaving them first-class language citizens.
Reply-to: Alex Gilding ([email protected]) Document No: N Revises Document No: N/A Date: 2021-10-
C requires that objects with static storage duration are only initialized with constant expressions. The rules for which kinds of expression may appear as constant expressions are quite restrictive and mostly limit users to using macro names for abstraction of values or operations. Users are also limited to testing their assertions about value behaviour at runtime because static_assert is similarly limited in the kinds of expressions it can evaluate at compile-time. We propose to add a new (old) specifier to C, constexpr, as introduced to C++ in C++11. We propose to add this specifier separately to objects and functions, and to intentionally keep the functionality minimal to avoid undue burden on lightweight implementations.
Because C limits initialization of objects with static storage duration to constant expressions, it can be difficult to create clean abstractions for complicated value generation. Users are forced to use macros, which do not allow for the creation of temporary values and require a different coding style. Such macros - especially if they would use temporaries, but have to use repetition instead because of the constraints of constant expressions - may also be unsuitable for use at runtime because they cannot guarantee clear evaluation of side effects. Macros for use in initializers cannot have their address taken or be used by linkage and are truly second-class language citizens. A user is obliged to repeat themselves and provide both a macro and a function (probably just deferring operation to the macro internally, but still unclear and verbose) if they wish for the same callable entity to be used in both a static context and a runtime context. The same restriction applies to static_assert: a user cannot prove properties about any expression involving a function call at compile-time, instead having to defer to runtime assertions. If a constant function could be called at compile-time, a release-mode build would be able to bake more testing of values and value-generation directly into the build step rather than relying on a separate debug configuration and test runs of the full program. C does provide enumerations which are marginally more useful than macros for defining constant values, but their uses are limited and they do not abstract very much; in practice they are only superior in the sense that they have a concrete type and survive preprocessing. Enumerations are not really intended to be used in this way.
A constexpr function is implicitly also an inline function, allowing it to be defined in a header. A constexpr function, called with arguments that are all themselves constant expressions, is a constant expression. A constexpr function may also be called with non-constant arguments and in that case behaves like any other function call. The address of a constexpr function may be taken and used as any other function pointer; this does not preserve the constexpr specifier. We currently propose to tighten the C++11 restrictions and prevent a constexpr function from calling itself recursively, for implementation-focused reasons. We do not propose changing the meaning of the const keyword in any way (this differs between C and C++) - an object declared at file scope with const and without static continues to have external linkage; an object declared with static storage duration and const but not constexpr is not considered any kind of constant-expression, barring any implementations that are already taking advantage of the permission given in 6.6 paragraph 10 to add more kinds of supported constant expressions. The difference between the behaviour of const in C and in C++ is unfortunate but is now cemented in existing practice and well-understood. We would oppose changing that now. We do not propose changing the meaning of the implied inline specifier on a constexpr function to match C++'s inline. A C constexpr function that wants to provide an externally linkable definition should use extern inline the same as current C inline functions do. No modifications are currently proposed for Section 7 as the Standard Library was not developed with the constexpr concept in mind. The specifier can be added on an individual basis to functions once the feature is available language-wide.
C currently has only one class of in-language entity that can be defined with a value and then used in a constant context, which is an enumeration. This is limited to providing a C-level name for a single integer value, but is extremely limited and is a second-class feature closer to macro constants than to C objects. These cannot be addressed and also cannot be used to help much in the abstraction of function-like expressions. GCC provides two non-standard attributes, const and pure, that are similar to this proposal. These attributes mostly communicate intent to calling code rather than impose restrictions on the function body itself. They are only applied to functions and do not substantially change the way C features can be used in expressions. const is closer to constexpr as it prevents the function from reading mutable state (pure merely says the function will not modify external state). n2539 "Unsequenced functions" by Alepin and Gustedt brought a number of proposed [[attributes]] that could annotate functions as having one of five levels of "unsequence", from full referential transparency (const) through to simply not leaking resources (noloeak). The stricter levels were a direct attempt to standardize the GNU attributes. Unfortunately an attribute-based solution does not provide the user-facing functionality of being able to simply use more complicated expressions within initializers and static assertions, because a
standard C attribute must leave the program correct when it is removed. If the constexpr nature of a function depends on an attribute, ignoring the attribute would change whether a program is valid at all. Therefore these attributes are mostly in aid of a slightly different goal of communicating more intent to the compiler about which external calls can be reordered or optimized away; they do not change the code a user can directly write. Some enhancements in the above proposal are also worth separate discussion:
The first question is of implementation burden. Barring recursive calls (or the possible extension to allow calls by pointer), a constexpr function call would be fully inlinable down to a fixed size expression tree which could then be evaluated the same way that an implementation currently evaluates a constant-expression with no function calls. Simple replacement of parameters by the argument values (as currently happens with macros used to abstract constant expressions) would be semantically correct. We consider this to be a modest implementation requirement. Allowing recursion would introduce more complicated interpreter-like behaviour on the part of the compiler's constant evaluator, requiring a stack or an interleaved evaluator/term-rewriter. We believe this is an unreasonable burden on small implementations and therefore would vote to restrict the use of recursion beyond what is allowed by C++11. Therefore, the rationale for prohibiting recursive calls is that without them, the expression tree for any constant expression can simply be fully inlined and expanded before evaluation begins, leaving a tree that can then be evaluated by current C constant evaluators with comparatively small changes. Allowing recursion would make this impossible and require at least some decision-making about control flow after parts of the tree had already started to be evaluated, bringing the evaluator closer to becoming a full-fledged interpreter. We consider this too demanding a request to impose on smaller compiler teams. As above, the existing incompatibility of const between C and C++ is preserved because the proposal does not intend to break or change any existing C code. Code that intends to express
type definitions (6.7.8) , function specifiers (6.7.4). Modify 6.7.4 "Function specifiers": Paragraph 1, add the constexpr specifier: function-specifier : inline _Noreturn constexpr Add three new paragraphs after paragraph 3: A function declared with the constexpr function specifier shall return a value. A function declared with the constexpr function specifier shall contain only: