Распакуйте вектор в аргументы для вызова функций с переменным числом аргументов - PullRequest
1 голос
/ 21 марта 2019

Немного сложно сформулировать этот вопрос.

Итак, скажем, у меня есть большое количество функций, у всех из которых есть различное количество аргументов - у некоторых их нет, у некоторых есть, у некоторых есть несколько или много других. Я получаю параметры для этих функций в векторе. Обычно я могу назвать их так:

 #include <vector>

 int my_functionA(std::string a) { /*...*/ return 1; }     
 int my_functionB(std::string a, std::string b) { /*...*/ return 2; }  

 void some_useful_function(std::vector<std::string> vec, int choice) {
      if (choice == 1) {
           // Error checking would happen here
           int val = my_functionA(vec[0]);
           // Do something with val
           // ...
      }
      else if (choice == 2) {
           // Error checking would happen here
           int val = my_functionB(vec[0], vec[1]);
           // Do something with val
           // ...
      }
 }

При проверке ошибок проверяется правильное количество аргументов и т. Д. Но это становится очень утомительным, если у меня большое количество функций, а проверки ошибок и то, что я делаю с возвращаемым значением, в основном совпадают ( т.е. проверяя, что размер вектора соответствует числу аргументов). В итоге я повторяю очень похожие ~ 15 строк кода 100 раз, и если я когда-нибудь решу, что хочу изменить или добавить что-то к этой 15-строчной «последовательности», мне придется повторить это сто раз.

Было бы более разумно, если бы я мог сделать карту из выбора в структуру данных, которая имеет указатель функции и другую информацию, которая мне понадобится, и тогда my_useful_function будет больше похожа на:

 struct funcPointer {
      funcPointer(void * f, int n) : fnc(f), numOfArgs(n) {};
      void * fnc;
      int numOfArgs;
 };

 std::map<int, funcPointer> = { 
      {1, funcPointer(my_functionA, 1)},
      {2, funcPointer(my_functionB, 2)}};

 void some_useful_function(std::vector<std::string> vec, int choice) {
      if (map.count(choice) > 0) {
           // Do stuff if map[choice].numOfArgs doesn't match vec size
           int val = map[choice].fnc(vectorSomehowConvertedToArguments);
           // Do stuff with the return value
      }
 }

Этот ответ с помощью «трюка с индексом» очень близко подошел ко мне, но, поскольку для unpack_caller требуется константа, я не уверен, как связать это с моей структурой карты / данных.

1 Ответ

5 голосов
/ 21 марта 2019

Во-первых, вот funcPointer, который возвращает лямбду, выполняющую жонглирование std::vector для аргументов перед вызовом данной функции, сгенерированное на основе арности функции:

template <class F, std::size_t... ParamsIdx>
auto funcPointer(F f, std::index_sequence<ParamsIdx...>) {
    return [f](std::vector<std::string> const &args) {
        assert(args.size() == sizeof...(ParamsIdx));
        return f(args[ParamsIdx]...);
    };
}

template <class... Params>
auto funcPointer(int (*f)(Params...)) {
    return funcPointer(f, std::index_sequence_for<Params...>{});
}

Эти лямбды могут бытьхранятся вместе в std::map<int, std::function<int(std::vector<std::string> const &)>>:

std::map<int, std::function<int(std::vector<std::string> const &)>> map = {
    {1, funcPointer(my_functionA)},
    {2, funcPointer(my_functionB)}
};

Наконец, вызов прост:

void some_useful_function(std::vector<std::string> vec, int choice) {
    if (map.count(choice) > 0) {
        int val = map[choice](vec);
        // Do stuff with the return value
        std::cout << "Call succeeded, got " << val << '\n';
    }
}

Посмотреть его в прямом эфире на Wandbox

...