1

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

Community
  • 1
  • 1
fuenfundachtzig
  • 7,952
  • 13
  • 62
  • 87
  • 1
    Why can't you just use an enum? Strings expose you to typos that won't get caught at compile-time. – Oliver Charlesworth Jan 31 '15 at 11:30
  • [What is the XY problem?](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Steve Jan 31 '15 at 12:01
  • `fun_new` is much faster than `fun_old` because it only (effectively) compares two characters of your string, while `fun_old` compares the full string... Also, [your function need not be executed at compile time](http://stackoverflow.com/questions/13346879/const-vs-constexpr-on-variables). – danielschemmel Jan 31 '15 at 12:20
  • @gha.st: That's a good point about why it's faster. (Though for `O1` it seems to do what I want.) You're right that `constexpr` is not strict, but more a suggestion to the compiler. – fuenfundachtzig Feb 03 '15 at 16:22
  • About enums: It's for convenience. I've talked to many people and most prefer string literals so that they don't have to update the enum (even though they do have to recompile that part anyway, and yes, we all now that enums are safer, but that's not my point). – fuenfundachtzig Feb 03 '15 at 16:24
  • Maybe I have talked too much about the "why" and should just have asked for a working compile-time hash for long strings ;) – fuenfundachtzig Feb 03 '15 at 16:25

0 Answers0