Skip to content

C++ switch with textual cases

C++ does not allow using string literals as case values in a switch statement. In modern C++ this restriction can be worked around with constexpr hash values.

E.g. instead of using string literals, we will be using their hash codes, computed during compilation and matched agains a hash code of a varable string.
First we define a hash function:

#pragma once
#include <cstdint>
#include <string_view>
namespace fnv1b {
/* FNV-1b hash function with extra rotation
 * https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
 */
template<typename T = std::size_t>
inline T hash(const char* str, std::size_t size) noexcept;

template<typename T>
inline constexpr T ror(T x, unsigned N) noexcept {
	return (x << (sizeof(T)*8 - N)) | ((x) >> (N));
}

template<>
inline constexpr uint32_t hash<uint32_t>(const char* str, std::size_t size) noexcept {
	constexpr uint32_t val32   = 0x811c9dc5;
	constexpr uint32_t prime32 = 16777619;

	uint32_t result = val32;
	while(size--) {
		result ^= uint32_t(*str);
		result  = ror(result, (8+(0xF&(*str++)))); /* added rotation 8-23 bits */
		result *= prime32;
	  }
	  return result;
}

template<>
inline constexpr uint64_t hash<uint64_t>(const char* str, std::size_t size) noexcept {
	constexpr uint64_t val64   = 0xcbf29ce484222325;
	constexpr uint64_t prime64 = 1099511628211ULL;
	uint64_t result = val64;
	while(size--) {
		result ^= uint64_t(*str);
		result  = ror(result, (8+(*str++)%48)); /* added rotation 8-55 bits */
		result *= prime64;
	}
	return result;
}

template<typename T = std::size_t>
inline constexpr T hash(const std::string_view str) noexcept {
	return hash<T>(str.data(), str.size());
}
}

Also available on gist

For easy of use we define a literal operator, computing hash from a string literal:

constexpr uint32_t operator "" _h(const char* str, size_t size) noexcept {
	return fnv1b::hash<uint32_t>(str, size);
}

Usage

#include <iostream>
#include <string>
#include "hash_fnv1b.hpp"
using namespace std;

int main() {
    auto s = "hello";
    switch(fnv1b::hash<uint32_t>(string_view(s))) {
        case "world"_h: cout << "World"; break;
        case "hello"_h: cout << "Hello"; break;
    }
    cout << endl;
}

This example is aslo available live at https://onlinegdb.com/BJydy3XvN

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

*