103

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

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Mircea Ispas
  • 20,260
  • 32
  • 123
  • 211
  • 3
    As long as the parameter is a constant expression, it must be. – chris Sep 17 '14 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? – T.C. Sep 17 '14 at 12:43
  • 13
    BTW, including `` and then calling `::printf` is non-portable. The standard only requires `` to provide `std::printf`. – Ben Voigt Sep 17 '14 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:) – Mircea Ispas Sep 17 '14 at 12:46
  • 1
    @BenVoigt Technically true, but is there a compiler which actually enforces that? – Angew is no longer proud of SO Sep 17 '14 at 12:47
  • Reading through [dcl.constexpr], the only thing I see is *A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that a call to a constexpr function can appear in a constant expression.* That doesn't sound too hopeful, but I don't see why it would be evaluated at runtime by any sane implementation anyway. – chris Sep 17 '14 at 12:47
  • Also see [this](https://stackoverflow.com/questions/13571749/where-in-the-c11-standard-does-it-specify-when-a-constexpr-function-can-be-eva) and [that](https://stackoverflow.com/questions/24322386/force-constexpr-to-be-evaluated-at-compile-time) question. – 5gon12eder Sep 17 '14 at 13:00
  • @Angew - there's nothing there to enforce. `#include ` is **allowed** to put names into the global namespace, but not required to. Similarly, `#include ` is allowed to put names into `std`, but not required to. I'd guess that `` putting names into the global namespace is common, but that `` putting names into `std` is not. – Pete Becker Sep 17 '14 at 14:29
  • @PeteBecker That's what I meant: "Is there a compiler whole `` does *not* put names in `::`?" – Angew is no longer proud of SO Sep 17 '14 at 17:06
  • In particular, such a compiler would be useful to partly enforce the admittedly grandiose goal that code should be "portable" (or even what C calls "strictly conforming"), by diagnosing a class of programs that certainly isn't. – Steve Jessop Sep 17 '14 at 19:13
  • 3
    @Felics I often use [godbolt](http://gcc.godbolt.org/#) when answering questions dealing with optimization and using `printf` can lead to significantly less code to deal with. – Shafik Yaghmour Sep 20 '14 at 17:58
  • If you can use a newer standard, https://en.cppreference.com/w/cpp/string/char_traits/length is constexpr since C++17 (and will likely perform an efficient strlen on runtime). – Kaznov Aug 31 '21 at 09:15

8 Answers8

83

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.
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 4
    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. – Steve Jessop Sep 17 '14 at 19:19
  • @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. – Shafik Yaghmour Sep 17 '14 at 19:36
  • 2
    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 ;-) – Steve Jessop Sep 17 '14 at 19:50
  • 3
    @SteveJessop Wilfully obstructive compilers, like the infamous (and unfortunately nonexistent) Hell++. Such a thing would actually be great for testing conformance/portability. – Angew is no longer proud of SO Sep 18 '14 at 08:07
  • 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. – Yakk - Adam Nevraumont Sep 24 '14 at 14:14
  • @Yakk as far as I can tell that is basically what Steve said above and I agreed in my comment the standard as written does not seem to support their interpretation. Two possible conclusions is that they are as Steve said only referring to *self-respecting compilers* or it is underspecified and the committees intention was as stated by Bjarne and Herb and this will be fixed in later drafts. I unfortunately can not tell you which it is, although I consider them to authoritative sources and practically their answers reflects implementations as they exist now. – Shafik Yaghmour Sep 24 '14 at 14:20
  • @ShafikYaghmour: If the code required to compute a value of a large type (e.g. "complex") would be smaller and faster than the code to load a constant, a compiler wouldn't have to be evil to compute a value at runtime. For the Standard to say that a compiler should compute constant expressions at compile time except when it would be more efficient to compute them at runtime would likely have been seen as condescending, though perhaps it would be helpful for standards to include something analogous to the U.S. Constitution's Ninth Amendment: the failure of the Standard to forbid something... – supercat May 11 '17 at 21:08
  • ...does not, in and of itself, imply that the action would be reasonable. The Standard makes no attempt to forbid every possible unreasonable way in which implementations might behave, and quality implementations should not require programmers to anticipate all possible forms of unreasonable behavior not anticipated by the Committee. – supercat May 11 '17 at 21:13
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;
}
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
25

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.

user2436830
  • 439
  • 3
  • 5
  • 6
    It can be not equal to strlen due to embedded '\0': strlen("hi\0there") != length("hi\0there") – unkulunkulu May 17 '16 at 09:19
  • 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](https://meta.stackexchange.com/a/45598), perhaps this will be more useful (and less C style). – QuantumKarl Mar 24 '17 at 09:44
  • @MikeWeir Ops, that is odd. Here are various links: [link to question](http://stackoverflow.com/questions/32287125/compile-time-string-encryption-using-constexpr), [link to paper](https://raw.githubusercontent.com/boostcon/cppnow_presentations_2012/master/wed/schurr_cpp11_tools_for_class_authors.pdf), [link to source on git](https://gist.github.com/creative-quant/6aa863e1cb415cbb9056f3d86f23b2c4#file-str_const-h) – QuantumKarl May 16 '17 at 18:15
  • now yow do : `char temp[256]; sprintf(temp, "%u", 2); if(1 != length(temp)) printf("Your solution doesn't work");` http://ideone.com/IfKUHV – Pablo Ariel Aug 24 '19 at 00:23
  • @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). – kkaja123 Jun 07 '22 at 18:57
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
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 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](http://goo.gl/hOYdYz)* – Shafik Yaghmour Sep 24 '14 at 16:37
  • `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 void foo();` and `foo();` *g++-4.8.4* – Aaron McDaid Jul 11 '15 at 14:52
  • @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. – Bulletmagnet May 30 '22 at 09:33
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.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • 9
    or just use std::integral_constant::value – Mircea Ispas Sep 17 '14 at 12:50
  • 1
    The example is a bit of a pointless use since `len` being `constexpr` means `length` must be evaluated at compile time anyway. – chris Sep 17 '14 at 12:51
  • @chris I didn't know it *must* be, though I have observed that it *is* with my compiler. – 5gon12eder Sep 17 '14 at 12:54
  • 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. – 5gon12eder Sep 17 '14 at 20:50
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.

  • `descriptionConsumed == "consumed"` gives `sizeof(descriptionConsumed) == 40` – bers Feb 06 '23 at 09:15
  • 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. – Hartmut Schorrig Feb 08 '23 at 23:33
  • 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. – bers Feb 09 '23 at 14:43
3

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

Silicomancer
  • 8,604
  • 10
  • 63
  • 130
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.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
kaedinger
  • 94
  • 6
  • These conditions **do not guarantee the returned value is constant**. For example, the function might be called with other argument values. – Ben Voigt Sep 17 '14 at 12:53
  • Right, @BenVoigt. I edited it to be called with a constant expression. – kaedinger Sep 17 '14 at 13:02