# handling different states of a complex gui control

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.

• Here's a better implementation. Nov 23 '21 at 8:59

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.

• 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. Nov 22 '21 at 15:01
• 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. Nov 22 '21 at 15:10
• 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. Nov 22 '21 at 16:00
• Maybe you can give an example or something. Nov 22 '21 at 16:13
• also, nothing prevents you from invoking dispatch() in different places. Nov 22 '21 at 17:06

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).

• very nice, but I may be unable to decrement i, as it may be an enum. Nov 22 '21 at 17:15
• 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. Nov 22 '21 at 17:20
• also, I usually try to avoid std::function, if I can. Nov 22 '21 at 17:24
• 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(*)(). Nov 22 '21 at 19:06
• 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. Nov 22 '21 at 22:41