1

Can we not "normalize" the behavior of a constexpr function by using is_constant_evaluated ? I understand why the successful cases work, but can't wrap my head around why the unsuccessful one doesn't.

gcc12.2 with -std=c++20 flag

error: the value of ‘str1’ is not usable in a constant expression constexpr auto numArgs = count(FMT); \ 
#include <memory> #include <iostream> #include <string.h> template<size_t arrSize> void print() { std::cout << "Number of format characters: " << arrSize << "\n"; } constexpr size_t countFormat(const char* format) { if(format[0] == '\0') return 0; return (format[0] == '%' ? 1u : 0u) + countFormat(format + 1); } constexpr size_t count(const char* format) { return std::is_constant_evaluated() ? countFormat( format ) : 0; } #define LOGMSG(FMT) { \ constexpr auto numArgs = count(FMT); \ print<numArgs>(); \ } int main() { const auto str1 = "Test %d %s"; constexpr auto str2 = "Test %d %s"; LOGMSG(str1); LOGMSG(str2); LOGMSG("Test %d %s"); return 0; } 
8
  • Try static const auto str1 = .... Then, str1 should be known at compile time (in your code, it isn't, because it's a stack-based variable). Commented Jan 3, 2023 at 22:49
  • Thanks Paul, but that doesn't work either. My goal is to make the function work even if the parameter is not known at compile time. That's why I have the is_constant_evaluated in my count function. Commented Jan 3, 2023 at 22:56
  • In LOGMSG(str1);, are you expecting std::is_constant_evaluated() to be true or false? If true, then it's a problem that str1 isn't constexpr. If false, how can you possibly expect to save the results into a constexpr variable? Commented Jan 3, 2023 at 22:57
  • @NathanPierson - I expect it to be false and return 0. Doesn't the compiler know I am returning 0 to the constexpr variable ? Commented Jan 3, 2023 at 23:02
  • 1
    @PaulSanders - I am trying to fix an existing codebase by not touching too many parts. I wrote some code so I could post it here and learn. Commented Jan 3, 2023 at 23:08

2 Answers 2

2

std::is_constant_evaluated is not a (magic) escape hatch when constant evaluation fails; generally it just tells you about the context in which you were called, with no ability to influence that context. (There is an exception for const variables that can be “promoted” to constexpr for C++03 compatibility, but that exception doesn’t do what you want and it does hurt everyone’s head, so let’s not.) It is possible to SFINAE on whether something is a constant expression, but only based on inputs that definitely are.

In general, constexpr programming doesn’t make C++ into an interpreted language; it’s a feature that it usually produces the same result as runtime evaluation, and the cases where it doesn’t are quite limited.

Moreover, constant evaluation has already failed in trying to call count, since it involves reading a (pointer) variable that isn’t available. (It might be possible to work around this issue by using references, but that doesn’t address the fundamental impossibility here.)

Sign up to request clarification or add additional context in comments.

5 Comments

but if the pointer is not available, shouldn't the compiler know that 0 can be still evaluated at compile time in the following return std::is_constant_evaluated() ? countFormat( format ) : 0; That's the part I am having a hard time to follow.
Certainly when the call isn’t occurring during constant evaluation, the 0 is the only thing used. What is the compiler supposed to do with it otherwise?
I expect the following to compile even if format is a pointer( and can't be constant evaluated). constexpr auto numArgs = std::is_constant_evaluated() ? countFormat( format ) : 0 Here either format can be constant evaluated and so passed to the function countFormat or the compiler ignores format and returns 0. Instead the compiler complains. That's the part I can't follow
@bramasub: How would std::is_constant_evaluated ever return false there? It’s a constexpr initializer!
Thanks Davis. I am beginning to see where I am going wrong here. Basically I want an expression to ignore format if it is not eligible for constant evaluation and return 0 instead. I will have to write SFINAE as you had recommended and see where that takes me
0

My goal is to make the function work even if the parameter is not known at compile time.

If constexpr function is evaluated in constant expression doesn't depend of its parameter (known or not at compile time) but from the context where the function is called.

in your case:

constexpr auto numArgs = count(FMT); 

count has to be evaluated as constant expression (so std::is_constant_evaluated() would be true).

When FMT is not known at compile time, then you will have error.

Your count implementation just "break" the runtime computation and doesn't allow the function to become constexpr when argument is not known at compile time.

You cannot branch depending of that argument "property".

Maybe you want overload on type constructed with consteval:

struct string_literal { consteval string_literal(const char* s) : s(s) {} const char* s; }; consteval size_t countFormat(string_literal format) { if(format.s[0] == '\0') return 0; return (format.s[0] == '%' ? 1u : 0u) + countFormat(format.s + 1); } consteval size_t count(string_literal format) { return countFormat(format); } constexpr size_t count(const char*const&) { return 0; } 

Demo.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.