7
\$\begingroup\$

I was about to use the awful enum+map trick to switch on std::strings when I thought to see if I could let some variadic templates do the job for me and I started to play...

#include <string>
#include <iostream>
#include <functional>
#include <unordered_map>

template<typename Key = std::string, typename Fun = std::function<void(void)> >
class Switcher {
  std::unordered_map<Key,Fun> map;
  Fun fdefault;
 public:
  template<typename... O>
  Switcher(const Fun& f, const Key& k, const Fun& f2, const O&... o): 
    Switcher(f2, o...) { map.emplace(k,f); }
  template<typename... O>
  Switcher(const Fun& f, const Key& k, const Key& k2, const O&... o):
    Switcher(f, k2, o...) { map.emplace(k,f); }

  Switcher(const Fun & f, const Key & k) { map.emplace(k,f); }
  Switcher(const Fun & f) { fdefault=f; }

  Switcher & operator()(const Key & k) {
    if (map.count(k)) map.at(k)();
    else if (fdefault) fdefault();
    return *this;
  }
};

int main() {
  std::string s1("hi");
  std::string s2("dear");
  Switcher<> switcher ( //whatever std::function here , all the keys here
                        [](){std::cout << "Hello ";  }, "hi",
                        [](){std::cout << "World ";  }, "dear", "buddy",
                        [](){std::cout << "Default ";}
                      );
  switcher(s1);
  switcher(s2);
  switcher("hi")("buddy")("?");
  return 0;
}

Of course is not the same as a switch statement, but it looks handy and for simple purposes it goes quite close, doesn't it?

\$\endgroup\$
1
\$\begingroup\$

While the idea is OK, this seems to add very little over a (unordered_)map of strings to functions (created directly). The client code is almost the same as your client code anyway:

int main() {
    typedef std::pair<std::string, std::function<void(void)>> pair;
    std::string s1("hi");
    std::string s2("dear");

    auto defaultFn = [](){ std::cout << "Default "; };

    std::map<std::string, std::function<void(void)>> switcher{
        pair{"hi", [](){ std::cout << "Hello "; } },
        pair{"dear", [](){ std::cout << "World "; } },
    }

    switcher[s1]();
    switcher[s2]();

    switcher["hi"](); // not supported: chaining calls ("buddy")("?");

    if(!switcher.count("buddy"))
        defaultFn();
    else
        switcher["buddy"]();
    return 0;
}

The code is a bit bigger, and some syntactic sugar is missing (stuff that you added in your example, like chaining calls and multiple keys per entry) but the code is semantically the same.

My point to adding this code: your class only makes sense if you use lots of switches (on strings) in your code, and usually in OOP, if you do have big switches in your code, you should extract them as a hierarchy of classes (or a table of functions) anyway.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.