5
\$\begingroup\$

Was thinking about extending the C switch beyond integers. Here is what it came to - will gladly accept any suggestions for improvement. Note: "Any class" in the title is not quite correct - the class should have equal operator and hash function to be used in this switch.

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

template<typename T>
using xmap = std::unordered_map<T, std::function<void()>>;

template<typename T>
void xwitch(const T& var, const xmap<T>& m, std::function<void()> otherwise = []() {})
{
    auto it = m.find(var);
    if (it == m.end())
        otherwise();
    else
        it->second();
}

#define SWITCH(var) xwitch(var, xmap<std::remove_const<decltype(str)>::type> {
#define END_SWITCH });
#define CASE {
#define THEN , []() {
#define END_CASE } },
#define DEFAULT }, []() {

int main()
{
    const std::string str{ "Second string" };
    using namespace std::string_literals;
    SWITCH(str)
        CASE "First string"s THEN std::cout << "First string matched\n"; END_CASE
        CASE "Second string"s THEN std::cout << "Second string matched\n"; END_CASE
        CASE "Third string"s THEN std::cout << "Third string matched\n"; END_CASE
    END_SWITCH
    
    SWITCH("unmatched"s)
        CASE "First string"s THEN std::cout << "First string matched\n"; END_CASE
        CASE "Second string"s THEN std::cout << "Second string matched\n"; END_CASE
        CASE "Third string"s THEN std::cout << "Third string matched\n"; END_CASE
        DEFAULT std::cout << "No matches\n";
    END_SWITCH
}
\$\endgroup\$
2
  • \$\begingroup\$ Technically this doesn't work on any class, since it requires the keys to implement operator== and std::hash<T>(...). But your question is valid regardless. \$\endgroup\$ Commented Jan 5 at 17:13
  • 2
    \$\begingroup\$ Why use macros? coliru.stacked-crooked.com/a/bdad77d62fe85c84 \$\endgroup\$ Commented Jan 5 at 17:45

1 Answer 1

3
\$\begingroup\$

Design

I don't think I'd use these macros in production, but it seems like a useful tool for thinking about a proposal for how a more general C++ switch might be implemented.

One improvement that could be made is to fall back to std::map if std::unordered_map can't be used (for totally-ordered unhashable objects).

One disadvantage we have compared to a solution at language level is that the map is constructed dynamically each time the SWITCH() is reached, rather than requiring the map to be constexpr and populating it during compilation. I don't see any easy way to rectify that. (I guess some users might consider this a feature, in that the cases can be computed at run-time, but I would prefer a visible map object for those.)

A serious weakness is that local variables aren't available to the code, because they are not captured in the lambdas. We might want to define THEN as , [&]{ to address that (I removed the unnecessary (), too).


Code

  • Missing include <type_traits>
  • In SWITCH() the type decltype(str) ought to be decltype(var).
  • <iostream> and <string> shouldn't be needed until main() - it's worth deferring them to show what's actually needed by SWITCH().
\$\endgroup\$
4
  • 1
    \$\begingroup\$ He can work around the dynamic construction by storing the value in a local variable, then constructing the map as static, and then invoking it. coliru.stacked-crooked.com/a/30d3a96dda0fa53c \$\endgroup\$ Commented Jan 5 at 17:55
  • \$\begingroup\$ Thanks, Toby, I do think it'll be best as the language-level improvement. As for compile-time map, I tried making it a template class with operator (), but it was way more complicated. \$\endgroup\$
    – johngo
    Commented Jan 9 at 0:16
  • \$\begingroup\$ Just checked - works fine without explicitly including type_traits. Well, at least in Visual Studio. \$\endgroup\$
    – johngo
    Commented Jan 9 at 0:24
  • \$\begingroup\$ Relying on things working without including the documented headers is a big portability risk (even to future versions of the same compiler). I recommend including headers that the standard says you need, rather than playing fast and loose like that. \$\endgroup\$ Commented Jan 11 at 8:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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