Страницы

Поиск по вопросам

среда, 29 января 2020 г.

Функция со switch с переменным количеством case

#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::map action; Когда контейнер заполнен использование может быть сокращено до вида: 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; } } 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...); } Функция 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 обработать пробросом исключения.

Комментариев нет:

Отправить комментарий