Computing length of a C string at compile time. Is this really a constexpr?

C++C++11StandardsConstexprString Literals

C++ Problem Overview


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...

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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;
}

Solution 3 - C++

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.

Solution 4 - C++

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

Solution 5 - C++

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.

Solution 6 - C++

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.

Solution 7 - C++

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.

Solution 8 - C++

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMircea IspasView Question on Stackoverflow
Solution 1 - C++Shafik YaghmourView Answer on Stackoverflow
Solution 2 - C++Ben VoigtView Answer on Stackoverflow
Solution 3 - C++user2436830View Answer on Stackoverflow
Solution 4 - C++Maxim EgorushkinView Answer on Stackoverflow
Solution 5 - C++5gon12ederView Answer on Stackoverflow
Solution 6 - C++kaedingerView Answer on Stackoverflow
Solution 7 - C++Hartmut SchorrigView Answer on Stackoverflow
Solution 8 - C++SilicomancerView Answer on Stackoverflow