Допустим, у меня есть несколько таких блоков:
while (true) {
// Ввод символа
do switch (_getch()) {
// Делать одно
case '1': dosmth1();
break;
// Делать второе
case '2': dosmth2();
break;
// Делать третье
case '3': dosmth3();
break;
// Выйти
case '0': dosmth0();
return 0;
// Повторить ввод
default: continue;
} while (false);
}
Но, ввиду частого создания оных, возникает вопрос:
Как организовать такую функцию menu()
, которая создавала бы n-ное количество case
, соответствующих n-ному количеству параметров (указателей на функции)?
Вызов такой функции по принципу stdarg
:
menu(4, dosmth1, dosmth2, dosmth3, dosmth0);
Если сигнатуры вызываемых функций одинаковы, то самый простой способ - использовать контейнер типа std::map
, std::unordered_map
. Ключом будет выступать char или int, а в качестве типа значения использоваться указатель на функцию. Например:
std::map<int, void(*)()> action;
Когда контейнер заполнен использование может быть сокращено до вида:
action[_getch()]();
Понятно, что в общем случае могут потребоваться дополнительные проверки на существование элемента, т.к. по умолчанию оператор []
приводит к созданию элемента, если его не было.
Несмотря на то что ответ уже принят, предложу вариант решения с помощью шаблонов с переменным числом аргументов (работает начиная с C++11):
typedef void(*ActionFunction)();
bool _menu(int condition, int action_counter, ActionFunction action)
{
if (action_counter == condition) {
action();
return true;
} else {
return false;
}
}
template<typename... Args>
bool _menu(int condition, int action_counter, ActionFunction action, Args... args)
{
if (action_counter == condition) {
action();
return true;
}
return _menu(condition, ++action_counter, args...);
}
template<typename... Args>
bool menu(int condition, ActionFunction action, Args... args)
{
return _menu(condition, 0, action, args...);
}
Функция menu
принимает номер функции, которую надо выполнить (действия нумеруются с нуля), и дальше любое количество указателей на функции. Если функции будут иметь разную сигнатуру, то надо просто дополнительно определить пару функций _menu
(шаблонную и обычную) с нужным типом аргумента в третьей позиции. Также функция menu
возвращает true
, если значение было найдено, и false
, если нет.
Пример (функции имеют сигнатуру void(*)()
):
#include <stdio.h>
void print0() { printf("0\n"); }
void print1() { printf("1\n"); }
void print2() { printf("2\n"); }
void print3() { printf("3\n"); }
typedef void(*ActionFunction)();
bool _menu(int condition, int action_counter, ActionFunction action)
{
if (action_counter == condition) {
action();
return true;
} else {
return false;
}
}
template<typename... Args>
bool _menu(int condition, int action_counter, ActionFunction action, Args... args)
{
if (action_counter == condition) {
action();
return true;
}
return _menu(condition, ++action_counter, args...);
}
template<typename... Args>
bool menu(int condition, ActionFunction action, Args... args)
{
return _menu(condition, 0, action, args...);
}
int main() {
menu(1, print0, print1, print2, print3); // вывод будет 1
menu('2' - 48, print0, print1, print2, print3); // вывод будет 2, так как в
// ascii символы цифр расположены начиная с 48 позиции (48 символ ascii это '0')
return 0;
}
Еще пример на ideone.
Как это работает: сначала вызывается menu
с аргументами, описанными выше.
Далее она вызывает _menu
, передавая ей свои аргументы + устанавливая счетчик в 0. Функция _menu
условно "откусывает" одну функцию action
от всех остальных, оставляя остальные в args
, потом смотрит, совпадает ли счетчик с условием, и, если да, выполняет action
и завершается, возвращая true
. Если условие не равно счетчику, то она увеличивает его и рекурсивно вызывает себя, передавая условие, увеличенный счетчик и оставшиеся функции. Если функция осталась одна, то вызывается обычная, а не шаблонная версия функции, и она возвращает false
, если условие и счетчик не равны.
Можно примерно вот таким подходом (пример на ideone):
#include <iostream>
#include <vector>
typedef void (*FuncType)();
typedef std::vector<FuncType> SwitchType;
void FuncOne() {
std::cout << "One" << std::endl;
}
void FuncTwo() {
std::cout << "Two" << std::endl;
}
int main() {
SwitchType Switch;
Switch.push_back(FuncOne);
Switch.push_back(FuncTwo);
Switch.push_back(FuncOne);
for(const auto i: {0,1,2}) Switch[i]();
return 0;
}
Вывод:
One
Two
One
Основная идея - заменить конструкцию switch
на вектор указателей на функции. Приведенный выше пример конечно незаконченный (default
секции нет), индексы строго по порядку. Но это все легко решается. К примеру, вектор можно заменить std::map
, а секцию default
обработать пробросом исключения.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Существует ли возможность создания 1-битных изображений и конвертации в этот формат изображений иных форматов с помощью библиотеки imagemagick?...
В игре присутствует модель игрокаСуть в том, что при нажатии W игрок просто летит вверх
Программа читает число с flash-памяти внешнего устройства (целое, 1 байт, беззнаковое)Для его программной интерпретации используется перечисление: