How to convert a string to a constant integer? - c++

I have strings which represent image names like "foobar.png" etc.
As you know, switch-case in C++ does not support switching on a string.
I'm trying to work around this, by hashing the string to std::size_t, and then using that value in the switch-case statements.
For example:
//frameName is an std::string which represents foobar.png etc..
switch (shs(frameName)) { //shs is my hash func which returns std::size_t;
case shs(Pfn::fs1x1): //Problem in this line
default:{
break;
}
}
In a separate file (Pfn.hpp):
namespace Pfn{
const std::string fs1x1 = "fs1x1";
};
The problem is, that in my case statement the compiler reports that shs(Pfn::fs1x1) is not a constant expression. The exact error message is:
Case value is not a constant expression:
It would be really tedious to work out all the hash-values in advance and then hardcode them into the case statements. Do you have a suggestion on how I can somehow create the constant expressions at runtime ?
EDIT: My shs function:
static std::size_t shs(std::string string){
return Hash::shs::hs(string);
}
//...
namespace Hash{
struct shs{
public:
inline std::size_t operator()(const std::string &string)const{
return hashString(string);
}
static std::size_t hs(const std::string &string){
std::size_t seed = 0;
hash_combine(seed,string);
return seed;
}
//From Boost::hash_combine.
template <class T>
static inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
};
};
}

shs's argument needs to be constexpr and shs itself must be constexpr as well. Chances are, you might want to provide different implementations for the compile-time version and the run-time version of the hash, due to C++11 constraints on constexpr functions.
I wrote a post on this very topic some time ago, using fnv1a as the hash algorithm. Here are the important parts:
constants:
typedef std::uint64_t hash_t;
constexpr hash_t prime = 0x100000001B3ull;
constexpr hash_t basis = 0xCBF29CE484222325ull;
Runtime hash:
hash_t hash(char const* str)
{
hash_t ret{basis};
while(*str){
ret ^= *str;
ret *= prime;
str++;
}
return ret;
}
Compile-time hash:
constexpr hash_t hash_compile_time(char const* str, hash_t last_value = basis)
{
return *str ? hash_compile_time(str+1, (*str ^ last_value) * prime) : last_value;
}
user defined string literal:
constexpr unsigned long long operator "" _hash(char const* p, size_t)
{
return hash_compile_time(p);
}
and usage:
switch(fnv1a_64::hash(str)){
case "first"_hash:
cout << "1st one" << endl;
break;
case "second"_hash:
cout << "2nd one" << endl;
break;
case "third"_hash:
cout << "3rd one" << endl;
break;
default:
cout << "Default..." << endl;
}
demo
But please, think of the children! Unless you can guarantee that there will be no hash collisions, this is playing with fire and is not fit to be production code.

I'll assume your hash won't have any collisions for purposes of this answer.
If you instead define your hash as a preprocessor function, you can create constants you can match to.
This post might help: Compile-time (preprocessor) hashing of string

By definition, constant expression means an expression that can be evaluated at compile time.
But you can use case func(): if func() is constexpr.

Related

Using boost mp11 to switch on runtime value efficiently(breaking when processing function is done)

I have the following code where I implement dispatching on runtime value to interpret the data in certain way(in this toy example data can be either uint8_t or short).
Code seems to work, but I am wondering if I can somehow microoptimize the code so that when I have a hit(processing function matches) processing is stopped (currently even if first element of tuple is a "handler" entire tuple is iterated over at runtime).
#include <boost/mp11/tuple.hpp>
#include <iostream>
uint8_t data[4] = {0,1,100,2};
template<int runtimeId, typename T>
struct kindToType{
static constexpr int id = runtimeId;
using type = T;
};
const auto print =[]<typename T> (const T* data){
if constexpr(std::is_same_v<short, std::remove_cvref_t<T>>){
const short* values = (const short*)data;
std::cout << values[0] << " " << values[1] << std::endl;
} else if constexpr(std::is_same_v<uint8_t, std::remove_cvref_t<T>>){
const uint8_t* values = (const uint8_t*)data;
std::cout << (int)values[0] << " " << (int)values[1]<< " " << (int)values[2] << " " << (int)values[3] << std::endl;;
}
};
static constexpr std::tuple<kindToType<10, uint8_t>, kindToType<11, short>> mappings{};
void dispatch(int kind){
boost::mp11::tuple_for_each(mappings, [kind]<typename Mapping>(const Mapping&) {
if (Mapping::id == kind)
{
print((typename Mapping::type*)data);
}
});
}
int main()
{
// no guarantee that kind is index like(e.g. for two values
// it can have values 47 and 1701)
dispatch(10);
dispatch(11);
}
Notes:
I can not/want to use std::variant.
I do not want to use std::map or std::unordered map(where value is std::function)
I know this is premature optimization(even 10 integer comparisons is cheap assuming handlers do nontrivial amount of work).
my handlers are unique, i.e. it is std::map like thing, not std::multimap like thing so it is fine to break;.
kind of id used for runtime values is not guaranteed to have values in [0, n-1].
I am fine with C++20 solution as long as it is implemented in at least 1 compiler.
The runtime performance of this heavily depends on the size of your tuple. You can make your own for_each_tuple implementation that does an early out when your function gets executed:
template<typename FuncTuple, typename Selector>
void tuple_for_each(FuncTuple const& funcTuple, Selector selector)
{
std::apply([selector](auto const& ...funcs)
{
(void)(selector(funcs) || ...);
}, funcTuple);
}
your dispatch would then look like this:
void dispatch(int kind)
{
tuple_for_each(mappings, [kind]<typename Mapping>(const Mapping&)
{
std::cout << "loop, ";
if (Mapping::id == kind)
{
print((typename Mapping::type*)data);
return true;
}
return false;
});
}
If you get rid of the template in your lambda and use auto instead this code will compile with C++17. We use operator short circuiting to our advantage so the compiler will provide an early out for us. Here is the full code.
Also, note that the cast (const short*)data is UB.

constexpr length of a string from template parameter

I am trying to obtain the length of a string passed as a template argument using C++11. Here is what I have found so far:
#include <iostream>
#include <cstring>
extern const char HELLO[] = "Hello World!!!";
template<const char _S[]>
constexpr size_t len1() { return sizeof(_S); }
template<const char _S[]>
constexpr size_t len2() { return std::strlen(_S); }
template<const char _S[], std::size_t _Sz=sizeof(_S)>
constexpr size_t len3() { return _Sz-1; }
template<unsigned int _N>
constexpr size_t len5(const char(&str)[_N])
{
return _N-1;
}
int main() {
enum {
l1 = len1<HELLO>(),
// l2 = len2<HELLO>() does not compile
l3 = len3<HELLO>(),
l4 = len3<HELLO, sizeof(HELLO)>(),
l5 = len5(HELLO),
};
std::cout << l1 << std::endl; // outputs 4
// std::cout << l2 << std::endl;
std::cout << l3 << std::endl; // outputs 3
std::cout << l4 << std::endl; // outputs 14
std::cout << l5 << std::endl; // outputs 14
return 0;
}
I am not very surprised with the results, I understand that the size of the array is lost in the case of len1() and len2(), although the information is present at compile time.
Is there a way to pass the information about the size of the string to the template as well? Something like:
template<const char _S[unsigned int _N]>
constexpr size_t len6() { return _N-1; }
[Edit with context and intent]
I gave up trying to concatenate a set of strings at compile time so I am trying to do it at initialization time. Writing something like a().b().c().str() would output "abc" while a().b().str() would output "ab"
Using templates, a().b() creates a type of B with a parent type A. a().b().c() creates a type C with a parent type B which has a parent type A, etc.
Given a type B with a parent A, this is a unique type and it can have it's own static buffer to hold the concatenation (this is why l5 isn't good for me). I could then strcpy` each one consecutively in a static buffer. I don't want to use a dynamically allocated buffer because my allocator is not necessarily configured at this point.
The size of that buffer which should be big enough to hold the string associated with A and the string associated with B is what I am trying to figure out. I can get it to work if I explicitly sizeof() as an extra template parameter (as done with l4 in the snippet above), but that make the whole code heavy to read and cumbersome to use.
[Edit 2] I marked the answer that was most helpful - but Yakk's answer was also good on gcc but except it did not compile with Visual Studio.
My understanding at this point is that we cannot rely on const char [] with external linkage to provide their size. It may work locally (if the template is compiled in the same unit as the symbol), but it won't work if the const char[] is in a header file to be used in multiple places.
So I gave up on trying to extract the length from the const char* template paramter and decided to live with l4 where the sizeof() is also provided to the template arguments.
For those who are curious how the whole thing turned out, I pasted a full working sample on ideone: http://ideone.com/A0JwO8
I can now write Path<A>::b::c::path() and get the corresponding "b.c" string in a static buffer at initialization.
constexpr std::size_t length( const char * str ) {
return (!str||!*str)?0:(1+length(str+1));
}
template<const char * String>
constexpr size_t len() { return length(String); }
extern constexpr const char HELLO[] = "Hello World!!!";
live example. Recursion wouldn't be needed in C++14.
To concatenate string at compile time,
with gnu extension, you may do:
template<typename C, C...cs> struct Chars
{
using str_type = C[1 + sizeof...(cs)];
static constexpr C str[1 + sizeof...(cs)] = {cs..., 0};
constexpr operator const str_type&() const { return str; }
};
template<typename C, C...cs> constexpr C Chars<C, cs...>::str[1 + sizeof...(cs)];
// Requires GNU-extension
template <typename C, C...cs>
constexpr Chars<C, cs...> operator""_cs() { return {}; }
template <typename C, C...lhs, C...rhs>
constexpr Chars<C, lhs..., rhs...>
operator+(Chars<C, lhs...>, Chars<C, rhs...>) { return {}; }
With usage
constexpr auto hi = "Hello"_cs + " world\n"_cs;
std::cout << hi;
Demo
Without gnu extension, you have to use some MACRO to transform literal into char sequence, as I do there.

How to construct a std::string with embedded values, i.e. "string interpolation"?

I want to create a string with embedded information. One way (not the only way) of achieving what I want is called string interpolation or variable substitution, wherein placeholders in a string are replaced with actual values.
In C, I would do something like this:
printf("error! value was %d but I expected %d",actualValue,expectedValue)
whereas if I were programming in python, I would do something like this:
"error! value was {0} but I expected {1}".format(actualValue,expectedValue)
both of these are examples of string interpolation.
How can I do this in C++?
Important Caveats:
I know that I can use std::cout if I want to print such a message to standard output (not string interpolation, but prints out the kind of string I want):
cout << "error! value was " << actualValue << " but I expected "
<< expectedValue;
I don't want to print a string to stdout. I want to pass a std::string as an argument to a function (e.g. the constructor of an exception object).
I am using C++11, but portability is potentially an issue, so knowing which methods work and don't work in which versions of C++ would be a plus.
Edit
For my immediate usage, I'm not concerned about performance (I'm raising an exception for cryin' out loud!). However, knowing the relative performance of the various methods would be very very useful in general.
Why not just use printf itself (C++ is a superset of C after all...)? This answer discusses some reasons why not. As far as I can understand, type safety is a big reason: if you put %d, the variable you put in there had better really be convertible to an integer, as that's how the function figures out what type it is. It would be much safer to have a method which uses compile-time knowledge of the actual type of the variables to be inserted.
In C++20 you will be able to use std::format.
This will support python style formatting:
string s = std::format("{1} to {0}", "a", "b");
There is already an implementation available: https://github.com/fmtlib/fmt.
Method 1: Using a string stream
It looks like std::stringstream gives a quick solution:
std::stringstream ss;
ss << "error! value was " << actualValue << " but I expected " << expectedValue << endl;
//example usage
throw MyException(ss.str())
Positive
no external dependencies
I believe this works in C++ 03 as well as c++ 11.
Negative
reportedly quite slow
a bit more messy: you must create a stream, write to it, and then get the string out of it.
Method 2: Boost Format
The Boost Format library is also a possibility. Using this, you would do:
throw MyException(boost::format("error! value was %1% but I expected %2%") % actualValue % expectedValue);
Positive
pretty clean compared to stringstream method: one compact construct
Negative
reportedly quite slow: uses the stream method internally
it's an external dependency
Edit:
Method 3: variadic template parameters
It seems that a type-safe version of printf can be created by using variadic template parameters (the techincal term for a template that takes an indefinite number of template parameters). I have seen a number of possibilities in this vein:
This question gives a compact example and discusses performance problems with that example.
This answer to that question, whose implementation is also quite compact, but reportedly still suffers from performance issues.
The fmt library, discussed in this answer, is reportedly quite fast and seems to be as clean as printf itself, but is an external dependency
Positive
usage is clean: just call a printf-like function
The fmt library is reportedly quite fast
The other options seem quite compact (no external dependency required)
Negative
the fmt library, while fast, is an external dependency
the other options apparently have some performance issues
In C++11 you can use std::to_string:
"error! value was " + std::to_string(actualValue) + " but I expected " + std::to_string(expectedValue)
It's not pretty, but it's straightforward, and you can use a macro to shrink it a bit. Performance is not great, since you do not reserve() space beforehand. Variadic templates would probably be faster and look nicer.
This kind of string construction (instead of interpolation) is also bad for localization, but you'd probably use a library if you needed that.
Use whatever you like:
1) std::stringstream
#include <sstream>
std::stringstream ss;
ss << "Hello world!" << std::endl;
throw std::runtime_error(ss.str());
2) libfmt : https://github.com/fmtlib/fmt
#include <stdexcept>
throw std::runtime_error(
fmt::format("Error has been detected with code {} while {}",
0x42, "copying"));
C++17 solution that works both for std::string & for std::wstring (Tested on VS2019 & VS2022):
#include <string>
#include <stdexcept>
#include <cwchar>
#include <cstdio>
#include <type_traits>
template<typename T, typename ... Args>
std::basic_string<T> string_format(T const* const format, Args ... args)
{
int size_signed{ 0 };
// 1) Determine size with error handling:
if constexpr (std::is_same_v<T, char>) { // C++17
size_signed = std::snprintf(nullptr, 0, format, args ...);
}
else {
size_signed = std::swprintf(nullptr, 0, format, args ...);
}
if (size_signed <= 0) {
throw std::runtime_error("error during formatting.");
}
const auto size = static_cast<size_t>(size_signed);
// 2) Prepare formatted string:
std::basic_string<T> formatted(size, T{});
if constexpr (std::is_same_v<T, char>) { // C++17
std::snprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
else {
std::swprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
return formatted; // Named Return Value Optimization (NRVO), avoids an unnecessary copy.
}
// USE EXAMPLE: //
int main()
{
int i{ 0 };
const std::string example1 = string_format("string. number %d.", ++i); // => "string. number 1."
const std::wstring example2 = string_format(L"wstring. number %d.", ++i); // => L"wstring. number 2."
}
DISCLAIMER:
The subsequent code is based on an article I read 2 years ago. I will find the source and put it here ASAP.
This is what I use in my C++17 project. Should work with any C++ compiler supporting variadic templates though.
Usage:
std::string const word = "Beautiful";
std::string const message = CString::format("%0 is a %1 word with %2 characters.\n%0 %2 %0 %1 %2", word, "beautiful", word.size());
// Prints:
// Beautiful is a beautiful word with 9 characters.
// Beautiful 9 Beautiful beautiful 9.
The class implementation:
/**
* The CString class provides helpers to convert 8 and 16-bit
* strings to each other or format a string with a variadic number
* of arguments.
*/
class CString
{
public:
/**
* Format a string based on 'aFormat' with a variadic number of arbitrarily typed arguments.
*
* #param aFormat
* #param aArguments
* #return
*/
template <typename... TArgs>
static std::string format(
std::string const&aFormat,
TArgs &&...aArguments);
/**
* Accept an arbitrarily typed argument and convert it to it's proper
* string representation.
*
* #tparam TArg
* #tparam TEnable
* #param aArg
* #return
*/
template <
typename TArg,
typename TEnable = void
>
static std::string toString(TArg const &aArg);
/**
* Accept a float argument and convert it to it's proper string representation.
*
* #tparam TArg
* #param arg
* #return
*/
template <
typename TArg,
typename std::enable_if<std::is_floating_point<TArg>::value, TArg>::type
>
static std::string toString(const float& arg);
/**
* Convert a string into an arbitrarily typed representation.
*
* #param aString
* #return
*/
template <
typename TData,
typename TEnable = void
>
static TData const fromString(std::string const &aString);
template <
typename TData,
typename std::enable_if
<
std::is_integral<TData>::value || std::is_floating_point<TData>::value,
TData
>::type
>
static TData fromString(std::string const &aString);
private:
/**
* Format a list of arguments. In this case zero arguments as the abort-condition
* of the recursive expansion of the parameter pack.
*
* #param aArguments
*/
template <std::size_t NArgs>
static void formatArguments(std::array<std::string, NArgs> const &aArguments);
/**
* Format a list of arguments of arbitrary type and expand recursively.
*
* #param outFormatted
* #param inArg
* #param inArgs
*/
template <
std::size_t NArgs,
typename TArg,
typename... TArgs
>
static void formatArguments(
std::array<std::string, NArgs> &aOutFormatted,
TArg &&aInArg,
TArgs &&...aInArgs);
};
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <typename... TArgs>
std::string CString::format(
const std::string &aFormat,
TArgs &&...aArgs)
{
std::array<std::string, sizeof...(aArgs)> formattedArguments{};
formatArguments(formattedArguments, std::forward<TArgs>(aArgs)...);
if constexpr (sizeof...(aArgs) == 0)
{
return aFormat;
}
else {
uint32_t number = 0;
bool readNumber = false;
std::ostringstream stream;
for(std::size_t k = 0; k < aFormat.size(); ++k)
{
switch(aFormat[k])
{
case '%':
readNumber = true;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// Desired behaviour to enable reading numbers in text w/o preceding %
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
if(readNumber)
{
number *= 10;
number += static_cast<uint32_t>(aFormat[k] - '0');
break;
}
default:
if(readNumber)
{
stream << formattedArguments[std::size_t(number)];
readNumber = false;
number = 0;
}
stream << aFormat[k];
break;
#pragma GCC diagnostic warning "-Wimplicit-fallthrough"
}
}
if(readNumber)
{
stream << formattedArguments[std::size_t(number)];
readNumber = false;
number = 0;
}
return stream.str();
}
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <typename TArg, typename enable>
std::string CString::toString(TArg const &aArg)
{
std::ostringstream stream;
stream << aArg;
return stream.str();
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <
typename TArg,
typename std::enable_if<std::is_floating_point<TArg>::value, TArg>::type
>
std::string CString::toString(const float& arg)
{
std::ostringstream stream;
stream << std::setprecision(12) << arg;
return stream.str();
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <std::size_t argCount>
void CString::formatArguments(std::array<std::string, argCount> const&aArgs)
{
// Unused: aArgs
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <std::size_t argCount, typename TArg, typename... TArgs>
void CString::formatArguments(
std::array<std::string, argCount> &outFormatted,
TArg &&inArg,
TArgs &&...inArgs)
{
// Executed for each, recursively until there's no param left.
uint32_t const index = (argCount - 1 - sizeof...(TArgs));
outFormatted[index] = toString(inArg);
formatArguments(outFormatted, std::forward<TArgs>(inArgs)...);
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <
typename TData,
typename std::enable_if
<
std::is_integral<TData>::value || std::is_floating_point<TData>::value,
TData
>::type
>
TData CString::fromString(std::string const &aString)
{
TData const result{};
std::stringstream ss(aString);
ss >> result;
return result;
}
//<-----------------------------------------------------------------------------
If you don't mind using a preprocessor script, here is a more easy but handy solution: https://github.com/crazybie/cpp_str_interpolation. Then you can write the code like this:
string s1 = "world", s2 = "!";
cout << _F("hello, {s1+s2}") << endl;
it also support using like a template engine:
int a = 1;
float b = 2.3f;
cout << _F(R"(
`for (int i=0; i<2; i++) {`
a is {a}, i is {i}.
a+i is {a+i}.
`}`
b is {b}.
cout << "123" << endl;`
)") << endl;
I've grown very fond of this solution, std::format notwithstanding. I dislike it on several counts (use of macros, and the whole concept of operator << overloading). But the ease of use truly makes up for it.
#ifndef SS_HPP
#define SS_HPP
#include <sstream>
#include <iostream>
// usage: SS("xyz" << 123 << 45.6) returning a std::string rvalue.
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str())
#endif

Compile-time hash function for long string (literals)

The problem I am trying to solve is that, for readability of my code, I would like to use string literals instead of numerals.
These should be converted to numerals at compile time (without additional preprocessing of the code).
In principle this should not be a problem nowadays, and actually the following seems to work:
constexpr unsigned long bogus_hash(char const *input) {
return input[0]+input[89];
}
constexpr unsigned long compute_hash(const char* a) {
return bogus_hash(a);
}
class HashedString {
public:
constexpr HashedString(const char* a): my_hash(compute_hash(a)) {};
constexpr HashedString(unsigned long a): my_hash(a) {};
constexpr unsigned long const& get() const {return my_hash;}
constexpr bool operator ==(const char* b) {
return my_hash == compute_hash(b);
};
protected:
unsigned long my_hash;
};
Almost: Of course, the hash function is use is not a good hash function. (The 89 in there is due to my test code:
int fun_new(HashedString a) {
return (a == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab");
}
int fun_old(std::string a) {
return (a == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab");
}
//#define CHOOSE fun_old
#define CHOOSE fun_new
int main() {
long res = 0;
for (long i = 0; i < 1000*1000*100; ++i) {
res += CHOOSE("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"); // will add 1
res += CHOOSE("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"); // will add 0
}
std::cout << "res = " << res << std::endl; // should return number of iterations in loop
return 0;
}
(Note that the internal use of hashes instead of the string literal is transparent to the caller of the function.)
My questions are:
Does the code actually do what I want it to? Timing the code shows that it does run much faster when using HashedString (fun_new) instead of std::string (fun_old). (I am using g++ 4.8.2, with options -std=c++0x -O0. Using -O1 seems to get rid of the loop completely?)
Is there a compile-time hash function I can use for this purpose? All other (constexpr) hash functions I tried made the code run slower than before (likely because the constexpr is not evaluated at compile but at run-time.) I tried to use these: 1, 2, 3

hash template metafunction and function

Any computation is possible at compile-time with C++ template metafunctions. Therefore, I was considering, if the following were possible:
void my_function(char const* string_ptr)
{
switch (hash_function(string_ptr))
{
case hash_metafunction<"yoohooo">::value:
...
break;
case hash_metafunction<"woooooo">::value:
...
break;
...
}
}
Can you give leads as to where to find code (library) for both the hash function and template metafunction. If none such library exists, can you give hints on how I might roll the template metafunction myself? I am particularly worried about the char const* parameter to the template metafunction. Maybe some preprocessor magic is possible?
How about a constexpr function? Of course implementing that hash could be a pain. You'll have something like this:
// maybe another return type
constexpr uint64_t hash_metafunction(const char* input) {
// replace some_value with the hash implementation
return some_value;
}
void my_function(char const* string_ptr)
{
switch (hash_function(string_ptr))
{
case hash_metafunction("yoohooo"):
...
break;
case hash_metafunction("woooooo"):
...
break;
...
}
}
The hash_metafunction function would be executed on compile-time.
Edit: This is a naive implementation, which basically converts the input string to a uint64_t:
constexpr uint64_t do_the_hash(const char* input, uint64_t value_so_far) {
return *input ? do_the_hash(input + 1, (value_so_far << 8) | *input) : value_so_far;
}
constexpr uint64_t hash_metafunction(const char* input) {
return do_the_hash(input, 0);
}
Live demo here.
Edit: I've implemented a compile time MD5, you can find the source code here. In order to use it, do the following:
#include <iostream>
#include "md5.h"
int main() {
constexpr auto value = ConstexprHashes::md5("constexpr rulz");
std::cout << std::hex;
for(auto v : value) {
if(((size_t)v & 0xff) < 0x10)
std::cout << '0';
std::cout << ((size_t)v & 0xff);
}
std::cout << std::endl;
}
This prints out the hash: "b8b4e2be16d2b11a5902b80f9c0fe6d6".
I have created a constexpr version of MurmurHash3 as a gist on GitHub.
int main() {
constexpr uint32_t hash = Murmur3_32("some_string_to_hash", 0xAED123FD);
assert(hash == 4291478129);
}