0

The following code is meant to create sort of a trivial hash of a string up to 8 characters long:

#include <type_traits>
#include <cstdint>
#include <iostream>

template<std::size_t N, std::size_t n=N>
constexpr typename std::enable_if<N<=9 && n==0,
uint64_t>::type string_hash(const char (&)[N])
{
    return 0;
}

template<std::size_t N, std::size_t n=N>
constexpr typename std::enable_if<N<=9 && n!=0,
uint64_t>::type string_hash(const char (&array)[N])
{
    return string_hash<N,n-1>(array) | ((array[n-1]&0xffull)<<(8*(n-1)));
}

For normal string literals and constexpr NULL-terminated strings it does indeed work normally. But if I do something like this:

constexpr char s2[] = {1,2,3,4,5,6,7,8,9};
std::cout << string_hash(s2) << "\n";

, the output will be the same as for the string "\x1\x2\x3\x4\x5\x6\x7\x8". I've tried adding a static_assert(array[N-1]==0,"Failed"); in the definition of string_hash, but the compiler says that array[N-1] is not a constant-expression. I then tried declaring the parameter constexpr, but the compiler said a parameter can't be declared constexpr.

How can I then do this check?

CygnusX1
  • 20,968
  • 5
  • 65
  • 109
Ruslan
  • 18,162
  • 8
  • 67
  • 136

2 Answers2

2

Please keep in mind that although constexpr functions can be used at compile time, they don't need to be. You cannot add any static assertions on runtime parameters, because the static assertion would be impossible to evaluate when the parameters are not known at compile time.

What you can do is the same thing you can do for non-constexpr functions: throw something. This doesn't prevent your function from being called with invalid input, but does prevent silent wrong results. And when your function is used in a context requiring a constant expression, the compiler will correctly detect it as not returning a constant value.

The body of a constexpr function needs to be a single return statement in C++11, but you can still fit it in there:

return array[N-1] ? throw "bad!" : <your current return expression here>;

Do pick something better to throw though.

  • Throwing will print an error only at run time. An error at compile time would probably be preferable. – CygnusX1 Aug 01 '15 at 16:13
  • @CygnusX1 I already covered that in my answer. If the function is used in a context where a constant expression is required, and invalid input is passed, then it will be rejected at compile time. If the function is used in a context where a constant expression is not required, then there is no way to force the compiler to generate an error message. You link to pages involving `assert` in your answer. What do you think `assert` does when the condition evaluates as false? That won't cause any compilation failure either. There's just no way of doing that in C++. –  Aug 01 '15 at 16:17
0

The reason why the compiler is complaining is because the array is unknown at the time when string_hash<N,n> function is being instantiated. static_cast is evaluated when instancing, and not when invoking (even if it is constexpr function).

Notice that there will be exactly one function created for each pair of <N,n> values. If you use two constexpr strings of same length, exactly same instance of string_hash will be used, but depending on the argument array[N-1] may give a different result.

I will continue to search for a precise answer to your question if it is needed. However, as a "quick fix", may I suggest changing the hash function so that it always include the last character in the computation, be it 0 or not?

Update: after doing some digging, I learned that some kind of constexpr_assert is what you probably want in your case and it is currently missing from the standard. Hopefully they will add it in the future. You may want to check:

Community
  • 1
  • 1
CygnusX1
  • 20,968
  • 5
  • 65
  • 109