简体   繁体   中英

String Literal As Template Argument

These preprocessor and template limitations of c++ are killing me. The goal is to convert string literals into integers.

template<const char* str>
inline int LiteralToInt(){
    return __COUNTER__;
}

using std::cout;

int main(){
    cout << LiteralToInt<"Hello">();
    cout << LiteralToInt<"No">();
    cout << LiteralToInt<"Hello">();
    return 0;
}

The output would be 010 if templates accepted string literals. Is there another way to get this output and convert string literals to integers at compile time?

Yes, C++ 11's constexpr will do this for you:

 constexpr int LiteralToInt(const char * str) {
      return __COUNTER__; // or whatever.
 }

Something like this would work

extern const char HELLO[] = "Hello";

and then

cout << LiteralToInt<HELLO>();

but not the literal itself. This is probably not what you want.

String literals themselves, as you already discovered, cannot be used as template arguments.

A little bit of thinking about Richard J Ross III's answer using constexpr gave me the right key to search with... What you're essentially doing is hashing a string at compile time. You can do this in C++11 (but not earlier versions) as shown here .

The basic idea is to use something like this:

unsigned int constexpr const_hash(char const *input) { 
    // really simple hash function...
    return static_cast<unsigned int>(*input) 
         && static_cast<unsigned int>(*input) + hash(input+1); 
}

But you probably want to use a hash function with more robust properties than this...

However if you're not using C++11 then my earlier statement holds:

No - there is no way to convert string literals to integers at compile time, in such a way that all of the same strings map to same values, (and different strings map to different values) across all compilation units, short of processing the code in some way.

Unfortunately, I know of no way to do exactly what you want.

Are there any restrictions you can put on the strings? Like number of characters? If you can limit it to 1-8 characters, you can do something like this:

template <char Ch1, char Ch2 = '\0', char Ch3 = '\0', char Ch4 = '\0', char Ch5 = '\0', char Ch6 = '\0', char Ch7 = '\0', char Ch8 = '\0'>
struct string_hash {
    static const uint64_t value = 
        (static_cast<uint64_t>(Ch1) << 56) | 
        (static_cast<uint64_t>(Ch2) << 48) | 
        (static_cast<uint64_t>(Ch3) << 40) | 
        (static_cast<uint64_t>(Ch4) << 32) | 
        (static_cast<uint64_t>(Ch5) << 24) | 
        (static_cast<uint64_t>(Ch6) << 16) | 
        (static_cast<uint64_t>(Ch7) << 8)  | 
        (Ch8);
};

which basically, at compile time stuff up to 8 characters into a uint64_t . Usage would look like this:

const uint64_t x = string_hash<'T', 'e', 's', 't'>::value

This will create a compile time numeric value (can be used in a switch and all that goodness) unique to each string 1-8 chars long. Unfortunately, the only big downside is that you can't write it as a string literal, you need to write it as a list of char s

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM