Date: Mon, 28 Apr 2025 09:48:02 -0400
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
}
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_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
}
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
Received on 2025-04-28 13:48:15