C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Idea and proposal: Concise mechanism for detecting specific constexpr arguments within functions

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Mon, 28 Apr 2025 15:55:58 +0200
pon., 28 kwi 2025 o 15:48 Arthur O'Dwyer via Std-Proposals
<std-proposals_at_[hidden]> napisał(a):
>
> On Mon, Apr 28, 2025 at 7:46 AM None via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>>
>> Problem Description
>> Consider the following function implementing "integer logarithm calculation" (, avoiding the need to define a separate function for special cases, and instead handling both general and exceptional cases within a single function):
>>
>> uint64_t myLog(uint64_t a, uint64_t b) {
>> // Calculate floor(log_b(a))
>> if (isPowerOfTwo(b)) {
>> // Optimized implementation for b being a power of 2
>> } else {
>> // Generic implementation
>> }
>> }
>>
>> When parameter b is known at compile time and is a power of 2, an efficient bit manipulation implementation can be used; but if b is not a compile-time constant, the condition check incurs runtime overhead.
>> This issue can be solved by using a non-type parameter template, but the function call must be written like "myLog<b>(a);", which looks unnatural and lacks consistency with "mylog(a, b)".
>> Existing "if consteval" can only execute a special path when all parameters are constexpr, but cannot optimize for cases where "only specific parameters are constexpr".
>>
>> Proposed Feature
>> I propose introducing a mechanism to detect whether specific parameters are constexpr. Possible syntax forms include (but are not limited to):
>>
>> // Possible syntax form 1a
>> if consteval (parameter_name) {
>
>
> This `if consteval (expr)` seems like the most natural syntax to me. It's fine grammar-wise, since `if consteval` wisely does not permit the following statement to start with anything but a curly brace.
> The semantics are very similar to GCC's `if (__builtin_constant_p(expr))`. It would help your proposal for you to study `__builtin_constant_p` and describe the similarities and the differences (if any).
> I see no reason to restrict the argument to just a lexical parameter name.
>
>> With this mechanism, the above example could be rewritten as:
>>
>> uint64_t myLog(uint64_t a, uint64_t b)
>> {
>> if consteval (b) {
>> if (isPowerOfTwo(b)) {
>> // Optimized implementation for b being compile-time known and a power of 2
>> return ...;
>> }
>> }
>> // Generic implementation
>> }
>
>
> Now, the big problem with this is that just knowing that `b`'s value is known to the compiler at compile-time — a boolean yes/no answer — doesn't give the programmer access to what numeric value the compiler thinks it has!
> So we still can't write, e.g.,
> uint64_t myLog(uint64_t a, uint64_t b)
> {
> if consteval (b) {
> std::array<int, b> subresults; // ERROR, the expression `b` did not magically become a constant expression in here... unless you propose that it should, which is a whole new can of worms
> // Optimized implementation with b known
> return ...;
> }
> // Generic implementation
> }
>

Even more:
```
if consteval (b) {
    return std::array<int, b>{};
}
```
How do we even handle this? Function would effectively change its type
based on values you pass to it.

> Another big problem for WG21 is that while the paper standard has a really solid notion of what it means to be globally, totally, in constant evaluation mode, it really has no notion that you could be in runtime mode and yet still know anything at all about variables' (now runtime) values. At the compiler level, sure, this is the well-known "constant propagation" optimization, which can feed into extensions like GCC's `__builtin_constant_p(expr)`. But at the paper standard level, we can't standardize semantics that depend on the specific compiler's optimizer.
> We need the following program's behavior to be dependable, so that we can put it in our standard-conforming compiler's unit test suite:
>
> int f(int x) { if consteval (x) { return 1; } else return 0; }
> int g(int x) { return f(x); }
> int main(int argc, char**) { return g(argc) + g(42); } // Return 0 + 0, or 0 + 1? This seems to depend on whether g is inlined or not. We can't have that.
>
> HTH,
> Arthur
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2025-04-28 13:56:14