15 Apr 2019 • C++ tricks: compile time string hashing

While writing that last post I figured I should write about this too.

We use it in Cocaine Diesel to generate a unique version number from the version string (git shorthash or tag if there is one). We also plan to use it for assets so we can refer to them by name in the code but not have it be super slow at runtime (could use an enum but that's annoying to maintain).

In C++11 constexpr functions can't have bodies besides a return statement, so we have to write the hashes as recursive functions and it's a big ugly. Here's FNV-1a:

constexpr uint32_t Hash32_CT( const char * str, size_t n, uint32_t basis = UINT32_C( 2166136261 ) ) {
	return n == 0 ? basis : Hash32_CT( str + 1, n - 1, ( basis ^ str[ 0 ] ) * UINT32_C( 16777619 ) );
}

constexpr uint64_t Hash64_CT( const char * str, size_t n, uint64_t basis = UINT64_C( 14695981039346656037 ) ) {
	return n == 0 ? basis : Hash64_CT( str + 1, n - 1, ( basis ^ str[ 0 ] ) * UINT64_C( 1099511628211 ) );
}

and then you can add some helper functions to make it a bit easier to use:

template< size_t N >
constexpr uint32_t Hash32_CT( const char ( &s )[ N ] ) {
	return Hash32_CT( s, N - 1 );
}

template< size_t N >
constexpr uint64_t Hash64_CT( const char ( &s )[ N ] ) {
	return Hash64_CT( s, N - 1 );
}

Errata: I missed the - 1 when I first wrote this. You need it so you don't hash the trailing '\0' char.