0
\$\begingroup\$

I need to handle different states of a complex GUI control (such as NORMAL, DRAGGING, MARKING, ...). I came upon this idea:

void dispatch(auto const i, auto&& ...f)
  noexcept(noexcept((f(), ...)))
{
  std::underlying_type<std::remove_const_t<decltype(i)>> j{};

  ((j++ == i ? (f(), 0) : 0), ...);
}

There would be an enum like this:

enum State
{
  NORMAL,
  DRAGGING,
  MARKING
};

and I would use dispatch() in event handlers like so:

dispatch(
  state_,
  []{},
  []{}
);

What do you think?

EDIT: This might help to prevent nested switches, which I find very hard to track.

\$\endgroup\$
1
  • \$\begingroup\$ Here's a better implementation. \$\endgroup\$ Nov 23 at 8:59
2
\$\begingroup\$

I don't get it.
j is the same type as the underlying type of i but without the const, and you could have made that clearer by using an explicit template parameter so you could leave the const out of the deduced type.
But the only thing you do is increment it, and since it started at 0, j++ just makes it 1. So why not just initialize it to 1 instead of incrementing zero?

This assumes that the type of enum used can be compared against an integer, so not the class style.

No wait a minute... you post increment j, which means it's 0 when you do the comparison. But you don't use j again, so what was the point of updating it?

Ah, I see... it's looped via the pack expansion of the comma operator. And you say switch is hard to follow???? You have three functions, and an index 0,1,2. You're doing a convoluted way to just index the list.

Instead, initialize a local array of three elements using a pack expansion, then subscript that.

\$\endgroup\$
5
  • \$\begingroup\$ the mechanics of doing the dispatch was not so important to me, as the idea of dispatch() itself. The various states and how they interact can be convoluted also. So, if you have an alternative to gigantic switches, I'd like to know. \$\endgroup\$ Nov 22 at 15:01
  • \$\begingroup\$ actually, it could be argued, that dispatch() should return a value, but that's just an implementation detail; I wanted to illustrate the general idea. \$\endgroup\$ Nov 22 at 15:10
  • \$\begingroup\$ In my designs (long time ago), such states are dynamically defined and handle any kind of modal or contextual behavior of the UI, not just universal things planned by the framework. A whole bundle of callbacks (e.g. all the mouse-related functions) are defined together, and to go into such a state, this handler is added to the front of the handler list. You don't have all the code for different states in one place like in the above design; they are truly independent and extensible. \$\endgroup\$
    – JDługosz
    Nov 22 at 16:00
  • \$\begingroup\$ Maybe you can give an example or something. \$\endgroup\$ Nov 22 at 16:13
  • \$\begingroup\$ also, nothing prevents you from invoking dispatch() in different places. \$\endgroup\$ Nov 22 at 17:06
1
\$\begingroup\$

You can avoid the complex declaration of j by just post-decrementing i:

void dispatch(auto i, auto&& ...f) noexcept(noexcept((f(), ...)))
{
  ((!i-- ? (f(), 0) : 0), ...);
}

But as JDługosz already pointed out, this is a very convoluted way of dispatching. Also, the compiler is generating quite bad code, equivalent to:

if (i++ == j)
    f[0]();
if (i++ == j)
    f[1]();
...

Why not keep it simple?

std::array<std::function<void()>, 3> functions{
  []{...},
  []{...},
  []{...}
};

...

functions[state_](); // call the given function

Of course, you want to add some bounds checking there. Your version would also have benefited from a check that i >= 0 && i < sizeof...(f).

\$\endgroup\$
8
  • 1
    \$\begingroup\$ very nice, but I may be unable to decrement i, as it may be an enum. \$\endgroup\$ Nov 22 at 17:15
  • \$\begingroup\$ also my implementation is not the best, an indices-based implementation would be better, but it was just an illustration. Indices would do no math. Also, in a gui, efficiency does not matter that much. \$\endgroup\$ Nov 22 at 17:20
  • \$\begingroup\$ also, I usually try to avoid std::function, if I can. \$\endgroup\$ Nov 22 at 17:24
  • \$\begingroup\$ Re std::function: I thought the use of auto rather than a typename T... explicit declaration meant there was only one type. But I'm not familiar with the new syntax, being stuck on old compilers for "real work". I suppose that's not the case, or you would not be able to pass different lambdas. So, the array issue has the problem of getting a common type. For this case, you can use the plain function pointer since it's a stateless lambda: void(*)(). \$\endgroup\$
    – JDługosz
    Nov 22 at 19:06
  • \$\begingroup\$ Ah yes, no std::function is needed in that case. Also, you can use a normal array instead of std::array I guess, which also helps it deduce the size automatically. \$\endgroup\$
    – G. Sliepen
    Nov 22 at 22:41

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.