I have a function that acts as a "function dispatch." This function contains a variable named next_func
, which is initialized to the first function to load. Then, inside an infinite loop, the next_func
is set to the return value of itself.
My question is, besides using auto
, what type does next_func
need to be? Here's some conceptual code (using auto
) to give an idea of what I'm looking for:
void FunctionDispatch()
{
auto next_menu = first_menu;
while (next_menu != app_exit)
{
next_menu = next_menu();
}
}
auto first_menu()
{
auto return_menu = first_menu; // Set to itself for 'auto', but I don't want to have to do this
std::cout << "2 or 3? ";
unsigned input = 0;
std::cin >> input;
switch (input)
{
case 2:
return_menu = second_menu;
break;
case 3:
return_menu = third_menu;
break;
default:
break;
}
return return_menu;
}
I like using auto
for trivial types, but I don't really like relying on it because I don't know how to address the type I'm wanting, which is why I'm wondering what auto
actually is here and how to explicitly declare the variables and function return types (probably using type aliases since that's most sensible).
Something to note:
FunctionDispath()
can call take no parameters and return function pointers to other functions that take in no parameters and return the same type of function pointers.First off, cool! This reminds me of middleware frameworks, or coroutines with tasks and event loops.
Doing this with straight-up function pointers will result in an infinitely recursive type, as many have mentioned. But if each task is a callable object, then you don't need recursive types as a forward reference will do. You can inherit std::function
to make it easy:
struct task : std::function<task()> {
using std::function<task()>::function;
};
Then you can assign functions to it. You can also bind arguments with std::bind
, or even use the default constructor to make an empty function with no target:
task n_hellos(int count) {
if (count) {
std::cout << "hello\n";
return std::bind(n_hellos, count-1);
}
return task();
}
You can cast an std::function
to bool to see if it is empty, allowing for a terminal case. The event loop below quits when the next task is empty:
int main() {
task current_task = std::bind(n_hellos, 5);
while (current_task) {
current_task = current_task();
}
} // prints "hello" five times