4
\$\begingroup\$

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.

asked Jun 3, 2013 at 20:54
\$\endgroup\$
3
  • \$\begingroup\$ Not sure I agree wiht: isn't an readability improvement over traditional if/else chain. or forget 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 true variable declaration spills over neighboring cases in C++. Anything with a constructor is bound into a case scope. Though true is essentially a computed goto you can use the same argument for for(;;), while(), if(){}else{} etc. Any control flow basically boils down to a computed goto. \$\endgroup\$ Commented Jun 5, 2013 at 0:58
  • \$\begingroup\$ @LokiAstari 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, by is essentially a computed goto I meant goto not on assembler level, but on a high-level language level. And the rant against break is because switch favours less common case (I want to fall-throgh) over more common case. \$\endgroup\$ Commented Jun 5, 2013 at 1:41
  • \$\begingroup\$ @LokiAstari I realise that my approach is flawed though - this function doesn't integrate so well with language, but I'm still looking for alternatives. \$\endgroup\$ Commented Jun 5, 2013 at 1:46

1 Answer 1

8
\$\begingroup\$

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?
answered Jun 4, 2013 at 6:04
\$\endgroup\$
1
  • \$\begingroup\$ Thank you for your opinion. Well, at first I tried to copy Pascal's case of statement, and halfway switched to VB's Select Case. So... yeah, maybe it's not that readable. \$\endgroup\$ Commented Jun 4, 2013 at 14:03

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.