#cpp #c
Допустим, у меня есть несколько таких блоков: // Ввод символа do switch (_getch()) { // Делать одно case '1': dosmth1(); break; // Делать второе case '2': dosmth2(); break; // Делать третье case '3': dosmth3(); break; // Выйти case '0': dosmth0(); return 0; } while (true); Но, ввиду частого создания оных, возникает вопрос: Как организовать такую функцию menu(), которая создавала бы n-ное количество case, соответствующих n-ному количеству параметров (указателей на функции)? Вызов такой функции по принципу stdarg: menu(4, dosmth1, dosmth2, dosmth3, dosmth0);
Ответы
Ответ 1
Если сигнатуры вызываемых функций одинаковы, то самый простой способ - использовать контейнер типа std::map, std::unordered_map. Ключом будет выступать char или int, а в качестве типа значения использоваться указатель на функцию. Например: std::mapaction; Когда контейнер заполнен использование может быть сокращено до вида: action[_getch()](); Понятно, что в общем случае могут потребоваться дополнительные проверки на существование элемента, т.к. по умолчанию оператор [] приводит к созданию элемента, если его не было. Ответ 2
Несмотря на то что ответ уже принят, предложу вариант решения с помощью шаблонов с переменным числом аргументов (работает начиная с C++11): typedef void(*ActionFunction)(); bool _menu(int condition, int action_counter, ActionFunction action) { if (action_counter == condition) { action(); return true; } else { return false; } } templatebool _menu(int condition, int action_counter, ActionFunction action, Args... args) { if (action_counter == condition) { action(); return true; } return _menu(condition, ++action_counter, args...); } template bool menu(int condition, ActionFunction action, Args... args) { return _menu(condition, 0, action, args...); } Функция menu принимает номер функции, которую надо выполнить (действия нумеруются с нуля), и дальше любое количество указателей на функции. Если функции будут иметь разную сигнатуру, то надо просто дополнительно определить пару функций _menu (шаблонную и обычную) с нужным типом аргумента в третьей позиции. Также функция menu возвращает true, если значение было найдено, и false, если нет. Пример (функции имеют сигнатуру void(*)()): #include 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 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 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, если условие и счетчик не равны. Ответ 3
Можно примерно вот таким подходом (пример на ideone): #include#include typedef void (*FuncType)(); typedef std::vector 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 обработать пробросом исключения.
Комментариев нет:
Отправить комментарий