I always considered switch
statement as somehow defective:
- works only on integral types and enumerations.
- isn't an readability improvement over traditional if/else chain.
- forget a
break
- you have a bug. - variable declaration spills over neighbouring cases.
- is essentially a computed goto
Because of said reasons, and also as an exercise on lambdas and variadic templates I created my own flow control function.
#include <functional>
#include <tuple>
template<typename V>
bool switch_on(const V& value)
{
return false;
}
template<typename V, typename P, typename... Args>
bool switch_on(const V& value, const P& p, Args... args)
{
if(std::get<0>(p)(value, std::get<1>(p)))
{
std::get<2>(p)();
return true;
}
else
{
return switch_on(value, args...);
}
}
template<template <typename> class P, typename F, typename V >
auto case_of(const V& v, const F& p) -> decltype( std::make_tuple(P<V>(), v, p) )
{
return std::make_tuple(P<V>(), v, p);
}
template<typename P, typename F, typename V >
auto case_of(const V& v, const F& p) -> decltype( std::make_tuple(P(), v, p) )
{
return std::make_tuple(P(), v, p);
}
template<typename F, typename V >
auto case_of(const V& v, const F& p) -> decltype( std::make_tuple(std::equal_to<V>(), v, p) )
{
return std::make_tuple(std::equal_to<V>(), v, p);
}
so I can use:
using std::less;
using std::greater;
int main()
{
int a = 42;
switch_on(a,
case_of<less>(0, [&]{
std::cout << "LESS THAN ZEROOOOOOOOOOOOOO.";
}),
case_of(42, [&]{
std::cout << "Yes";
}),
case_of<greater>(9000, [&]{
std::cout << "IT'S OVER NINE THOUSAAAAAAAAND!!!";
})
);
}
While in Lisp, it is encouraged to create new forms of flow control, what about C++? I would also like to see some opinions about template usage, some pointers on how to improve the code and possible corner cases when this code will break.
1 Answer 1
Lisp has an uniform syntax, so it’s possible to create user defined flow control constructs that look exactly like the built in constructs. In C++, that’s generally not possible.
Your code is certainly clever, and it solves the problem of omitted breaks (I consider the other drawbacks you list less important). However:
- Despite naming this a "switch", you’ve essentially recreated an if-else chain. Notably, a compiler may have a bit more difficulty optimizing this than a traditional switch.
- Your implementation does not seem to support range comparisons or a default case, although both of these seem to be doable in your conceptual framework.
- When used in a disciplined way, falling through from one switch case to the next is not always devoid of merit.
- I’m not sure that returning this boolean has a great benefit. It might be preferable to design a construct that ensures that one of the cases is always executed.
- Be honest: do you really find this more readable than a traditional switch statement?
-
\$\begingroup\$ Thank you for your opinion. Well, at first I tried to copy Pascal's
case of
statement, and halfway switched to VB'sSelect Case
. So... yeah, maybe it's not that readable. \$\endgroup\$milleniumbug– milleniumbug2013年06月04日 14:03:30 +00:00Commented Jun 4, 2013 at 14:03
isn't an readability improvement over traditional if/else chain.
orforget a break - you have a bug
lot of functionality for fall through and when you do need it the compiler will warn you about it being missing so not a real problem. Don't believe this is truevariable declaration spills over neighboring cases
in C++. Anything with a constructor is bound into a case scope. Though trueis essentially a computed goto
you can use the same argument forfor(;;)
,while()
,if(){}else{}
etc. Any control flow basically boils down to a computed goto. \$\endgroup\$when you do need it the compiler will warn you about it being missing so not a real problem
. Not sure what you mean here - can compiler distinguish when I need fall-through?Anything with a constructor is bound into a case scope.
Well, apparently only if you use braces to introduce one, that is, byis essentially a computed goto
I meantgoto
not on assembler level, but on a high-level language level. And the rant againstbreak
is becauseswitch
favours less common case (I want to fall-throgh) over more common case. \$\endgroup\$