In general, it's not possible to build a function mapping arbitrary-length strings into a fixed domain. That violates the pigeonhole principle.
The following suggestion seems to me fairly convoluted, but given the lack of larger context to your problem, here goes...
Suppose you build a class through which to run all your names, as so
class compressor {
explicit compressor(std::size_t seed);
std::string operator()(const std::string &name) const;
}
It has two members: a ctor taking a seed, and an operator()
taking a name string and returning an 8-char key string. In your code, initialize this object with some fixed, arbitrary seed.
Internally, the class object should hold an unordered_map
mapping, for each distinct name on which it was applied, the key to which it was mapped. Initially, obviously, this internal unordered_map
will be empty.
The class object should use a universal hash function, pseudo-randomly selected by the seed
in the constructor. See the answer to this question on one way to create a universal hash function.
When the operator is called, it should check if the name is in the internal unordered_map
. If so, return the key found for it. Otherwise, first use the hash function to calculate the key and place it in the internal unordered_map
. When generating a new key, though, check if it collides with an existing key, and throw an exception if so.
Realistically speaking, since each distinct name corresponds to a place in your code where you call typeid
, the number of distinct names, say n should be in the 1000s, at most. Set m to be the range possible with 8 characters (264).
The probability of a collision is ~n2 / (2 m), which should be tiny. Thus, most chances are that there will be no collisions, and no exception thrown. If one is thrown, though, change the seed, and build the program again. The expected number of times you'll have to do that (after the initial time) is close to 0.