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.
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. – Loki Astari Jun 5 '13 at 0:58when 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. – milleniumbug Jun 5 '13 at 1:41