107

I'm trying to compute the length of a string literal at compile time. To do so I'm using following code:

#include <cstdio> int constexpr length(const char* str) { return *str ? 1 + length(str + 1) : 0; } int main() { printf("%d %d", length("abcd"), length("abcdefgh")); } 

Everything works as expected, the program prints 4 and 8. The assembly code generated by clang shows that the results are computed at compile time:

0x100000f5e: leaq 0x35(%rip), %rdi ; "%d %d" 0x100000f65: movl $0x4, %esi 0x100000f6a: movl $0x8, %edx 0x100000f6f: xorl %eax, %eax 0x100000f71: callq 0x100000f7a ; symbol stub for: printf 

My question: is it guaranteed by the standard that length function will be evaluated compile time?

If this is true the door for compile time string literals computations just opened for me... for example I can compute hashes at compile time and many more...

12
  • 3
    As long as the parameter is a constant expression, it must be. Commented Sep 17, 2014 at 12:42
  • 2
    @chris Is there a guarantee that something that can be a constant expression must be evaluated at compile time when used in a context that doesn't require a constant expression? Commented Sep 17, 2014 at 12:43
  • 13
    BTW, including <cstdio> and then calling ::printf is non-portable. The standard only requires <cstdio> to provide std::printf. Commented Sep 17, 2014 at 12:43
  • 1
    @BenVoigt Ok, thanks for pointing that out:) Initially I used std::cout, but the generated code was pretty big to find the actual values:) Commented Sep 17, 2014 at 12:46
  • 3
    @Felics I often use godbolt when answering questions dealing with optimization and using printf can lead to significantly less code to deal with. Commented Sep 20, 2014 at 17:58

8 Answers 8

84

Constant expressions are not guaranteed to be evaluated at compile time, we only have a non-normative quote from draft C++ standard section 5.19 Constant expressions that says this though:

[...]>[ Note: Constant expressions can be evaluated during translation.—end note ]

You can assign the result to constexpr variable to be sure it is evaluated at compile time, we can see this from Bjarne Stroustrup's C++11 reference which says (emphasis mine):

In addition to be able to evaluate expressions at compile time, we want to be able to require expressions to be evaluated at compile time; constexpr in front of a variable definition does that (and implies const):

For example:

constexpr int len1 = length("abcd") ; 

Bjarne Stroustrup gives a summary of when we can assure compile time evaluation in this isocpp blog entry and says:

[...]The correct answer - as stated by Herb - is that according to the standard a constexpr function may be evaluated at compiler time or run time unless it is used as a constant expression, in which case it must be evaluated at compile-time. To guarantee compile-time evaluation, we must either use it where a constant expression is required (e.g., as an array bound or as a case label) or use it to initialize a constexpr. I would hope that no self-respecting compiler would miss the optimization opportunity to do what I originally said: "A constexpr function is evaluated at compile time if all its arguments are constant expressions."

So this outlines two cases where it should be evaluated at compile time:

  1. Use it where a constant expression is required, this would seem to be anywhere in the draft standard where the phrase shall be ... converted constant expression or shall be ... constant expression is used, such as an array bound.
  2. Use it to initialize a constexpr as I outline above.
Sign up to request clarification or add additional context in comments.

8 Comments

That said, in principle a compiler is entitled to see an object with internal or no linkage with constexpr int x = 5;, observe that it doesn't require the value at compile-time (assuming it isn't used as a template parameter or whatnot) and actually emit code that computes the initial value at runtime using 5 immediate values of 1 and 4 addition ops. A more realistic example: the compiler might hit a recursion limit and defer computation until runtime. Unless you do something that forces the compiler to actually use the value, "guaranteed to be evaluated at compile time" is a QOI issue.
@SteveJessop Bjarne seems to be using a concept which does not have an analogue I can find in the draft standard which is used as a constant expression means evaluated at translation. So it would seem that the standard does not explicitly state what he is saying, so I would tend to agree with you. Although both Bjarne and Herb seem to agree on this, which could indicate it is just underspecified.
I think they're both considering only "self-respecting compilers", as opposed to the standards-conforming but wilfully obstructive compiler I hypothesise. It's useful as a means of reasoning about what the standard actually guarantees, and not much else ;-)
@SteveJessop Wilfully obstructive compilers, like the infamous (and unfortunately nonexistent) Hell++. Such a thing would actually be great for testing conformance/portability.
Under the as-if rule, even using the value as a seeming compile time constant is not enough: the compiler is free to ship a copy of your source and recompile it at runtime, or do ru ti e calculation to determine the type of a variable, or just pointlessly rerun your constexpr calculation out of sheer evil. It is even free to wait 1 second per character in a given line of source, or take a given line of source and use it to seed a chess position, then play both sides to determine who won.
|
28

It's really easy to find out whether a call to a constexpr function results in a core constant expression or is merely being optimized:

Use it in a context where a constant expression is required.

int main() { constexpr int test_const = length("abcd"); std::array<char,length("abcdefgh")> test_const2; } 

8 Comments

... and compile with -pedantic, if you use gcc. Otherwise, you get no warnings and errors
@BЈовић Or use it in a context where GCC has no extensions potentially getting in the way, such as a template argument.
Wouldn\t an enum hack be more reliable? Such as enum { Whatever = length("str") } ?
Worthy of mention is static_assert(length("str") == 3, "");
constexpr auto test = /*...*/; is probably the most general and straightforward.
|
28

Let me propose another function that computes the length of a string at compile time without being recursive.

template< size_t N > constexpr size_t length( char const (&)[N] ) { return N-1; } 

Have a look at this sample code at ideone.

5 Comments

It can be not equal to strlen due to embedded '\0': strlen("hi\0there") != length("hi\0there")
This is the correct way, this is an example in Effective Modern C++ (if I recall correctly). However, there is a nice string class that is entirely constexpr see this answer: Scott Schurr's str_const, perhaps this will be more useful (and less C style).
@MikeWeir Ops, that is odd. Here are various links: link to question, link to paper, link to source on git
now yow do : char temp[256]; sprintf(temp, "%u", 2); if(1 != length(temp)) printf("Your solution doesn't work"); ideone.com/IfKUHV
@PabloAriel Using sprintf() is not constexpr, so the fact that length() does not return the expected string length is not relevant for this question. Additionally, the char array is not constexpr, so there wouldn't be any point to getting its size at compile time if the string it contains can be of variable length at run time (unless you're using it to measure the max character capacity).
21

Just a note, that modern compilers (like gcc-4.x) do strlen for string literals at compile time because it is normally defined as an intrinsic function. With no optimizations enabled. Although the result is not a compile time constant.

E.g.:

printf("%zu\n", strlen("abc")); 

Results in:

movl $3, %esi # strlen("abc") movl $.LC0, %edi # "%zu\n" movl $0, %eax call printf 

3 Comments

Note, this works because strlen is a built-in function, if we use -fno-builtins it reverts to calling it at run-time, see it live
strlen is constexpr for me, even with -fno-nonansi-builtins (seems like -fno-builtins doesn't exist in g++ any more). I say "constexpr", because I can do this template<int> void foo(); and foo<strlen("hi")>(); g++-4.8.4
@ShafikYaghmour strlen is not a built-in function, it's a function defined by the standard C library, and there's no constexpr in the C language.
7

There is no guarantee that a constexpr function is evaluated at compile-time, though any reasonable compiler will do it at appropriate optimization levels enabled. On the other hand, template parameters must be evaluated at compile-time.

I used the following trick to force evaluation at compile time. Unfortunately it only works with integral values (ie not with floating point values).

template<typename T, T V> struct static_eval { static constexpr T value = V; }; 

Now, if you write

if (static_eval<int, length("hello, world")>::value > 7) { ... } 

you can be sure that the if statement is a compile-time constant with no run-time overhead.

4 Comments

or just use std::integral_constant<int, length(...)>::value
The example is a bit of a pointless use since len being constexpr means length must be evaluated at compile time anyway.
@chris I didn't know it must be, though I have observed that it is with my compiler.
Ok, according to the majority of other answers it has to, so I have modified the example to be less pointless. In fact, it was an if-condition (where it was essential the compiler did dead code elimination) for which I originally used the trick.
6

very simple:

sizeof("myStringLiteral") does the work.

the sizeof intrinsic compiler function evaluates guaranteed on compile time. It is a powerful compiler feature, often underestimated. It works on C++ and also C.

Note: You may need to convert from size_t to int, and subtract 1, both also done on compile time:

int test_sizeof_text = (int)(sizeof("1234567")-1);

sizeof("text") is the size inclusively the terminating 0, hence -1 for the number of character.

3 Comments

descriptionConsumed == "consumed" gives sizeof(descriptionConsumed) == 40
What is descriptionConsumed? which type? The sizeof("xyz") works only for string literals, not for data which stores strings. But you can set the length in the moment of fillin with a literal.
I see! I used it on a const std::string initialized from a static constexpr const char *. sizeof works correctly on the latter, but not the former, which you explain nicely.
5

Starting with C++20 you can use consteval instead of constexpr to enforce compile time execution of a function.

Comments

1

A short explanation from Wikipedia's entry on Generalized constant expressions:

The use of constexpr on a function imposes some limitations on what that function can do. First, the function must have a non-void return type. Second, the function body cannot declare variables or define new types. Third, the body may contain only declarations, null statements and a single return statement. There must exist argument values such that, after argument substitution, the expression in the return statement produces a constant expression.

Having the constexpr keyword before a function definition instructs the compiler to check if these limitations are met. If yes, and the function is called with a constant, the returned value is guaranteed to be constant and thus can be used anywhere a constant expression is required.

2 Comments

These conditions do not guarantee the returned value is constant. For example, the function might be called with other argument values.
Right, @BenVoigt. I edited it to be called with a constant expression.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.