Home > OS >  How to convert a std::string to const char* or char* at compile-time
How to convert a std::string to const char* or char* at compile-time

Time:11-01

Why yet another question on string to hash conversion

My question is basically the same as the popular How to convert a std::string to const char* or char* question, but with a twist. I need the hash at compile time. Before rejecting my question, let me briefly explain my motivation.

Motivation

In my framework I am building, I have many threads. I have carefully designed the file structure so these threads re-use files whose functionality are the same so as not to violate ODR and lose maintainability. At the top of the list is error logging. My elaborate code begs to be re-used as-is in these different apps. So the initialized errorLogger object needs to be a different instance for each thread.

Proposed solution

Templatize my ErrorLogger class with a constant non-type parameter. In my framework, each app has a unique string that identifies itself. Now if I could hash that string at compile time, I would have the non-type template parameter I need for the compiler to generate separate instances.

Here is the example code that doesn't work:

#include <string>

std::string threadUniqueStr { "threadUniqueName" };
/*constexpr*/ auto threadUniqueHash = std::hash< std::string > {} ( threadUniqueStr ); // uncommented constexpr results in 'expression did not evaluate to a constant'

template< size_t >
void Func()
{}

int main()
{
  //Func< threadUniqueHash >(); // ERROR: expression did not evaluate to a constant
  Func< 33 >(); // works fine
}

But maybe there is an easier C way to do this that I am overlooking?

Edit 1: My Solution

Answer 2 shows how to create a hash from a string using string_view which follows @NathanOliver's advice that you have to write your own hash function for it to be constexpr. But I understand that writing your own hash function can have problems. @Pepijn Kramer points out 1) two strings may still produce the same hash and 2) from his experience, that a class hierarchy featuring app reporting at the top and individual error reporting behavior derived classes served his purposes in multi-dev situations (like mine). Since I don't want to use templates non-type parameter feature in an un-tried manner even though I can make a case for it, I am going to create my own ErrorLogger class hierarchy. Thanks to all for your helpful input.

CodePudding user response:

In C 17 a string_view can be constexpr so you can make your own hash function that takes one of those e.g. the hash function from someone's answer here would be like the following.

#include <string_view>
#include <iostream>

constexpr size_t some_hash(std::string_view sv) {
    size_t  hash = 5381;
    for (auto c : sv)
        hash = ((hash << 5)   hash)   c; 

    return hash;
}

template<size_t N>
void some_function() {
    std::cout << N << "\n";
}

int main() {
    some_function<some_hash("foobar")>();
}

CodePudding user response:

The essence of the problem is this:

template and constexpr are required to be evaluated at compile time, std::hash is calculated at run-time and is external to the compiler.

You can say the result of the hash is const, but it seems like you want to tell the compiler to run some function. compilers usually don't do that.

  • Related