Reputation: 223
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?
Upvotes: 3
Views: 3603
Reputation: 73580
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.
Upvotes: 1
Reputation: 90543
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
Upvotes: 0
Reputation: 320747
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.
Upvotes: 1
Reputation: 55583
Yes, C++ 11's constexpr
will do this for you:
constexpr int LiteralToInt(const char * str) {
return __COUNTER__; // or whatever.
}
Upvotes: 3