#c #c 11 #variadic-functions
Вопрос:
Мой коллега написал очень длинную функцию переключения, как показано ниже:
void func(int type, int a, int b) {
std::string str = "hello";
switch (type) {
case 1: {
func1(a, b);
break;
}
case 2: {
func2(a, b, str);
break;
}
// hundreds of cases...
}
}
Я хочу изменить эту ужасную функцию, потому что в ней слишком много случаев.
Хорошей новинкой является то, что в каждом случае вызывается только одна функция, которая должна использовать a
и b
в качестве своего первого и второго параметров. Плохая новость заключается в том, что некоторым из них может потребоваться третий параметр.
Я думаю, смогу ли я использовать a std::map<int, std::function<void(int, int, ...)>>
для хранения всех функций в случаях, но это не работает. Кажется, что это не std::function
может принять вариативную функцию.
Есть ли какой — то другой способ сделать это?
Комментарии:
1. Откуда берутся дополнительные аргументы? Для тех, кто может использовать лямбду для создания функции, занимающей два входа. Но в конечном счете большой переключатель будет более эффективным. Вы можете использовать массив указателей функций вместо карты.
2. @Бен, я переиздал свой вопрос. Все дополнительные аргументы определены в функции
func
.3. Мне было более любопытно, какие функции имеют сколько дополнительных аргументов? Это всего лишь один или два или около того, большинство из которых требуют одного или нескольких дополнительных аргументов?
4. @Бен Есть только два вида функций: одна есть
void(int, int)
, другая естьvoid(int, int, ???)
. На данный момент,???
это всегдаstd::string
так .5. @Ben Я не могу точно сказать, сколько функций принимает два аргумента и сколько функций принимает три функции. Потому что это может измениться в любое время.
Ответ №1:
std::any
будет твоим другом. Вместе с некоторым классом-оболочкой и функцией шаблона в классе-оболочке, чтобы скрыть любое приведение, это будет несколько более интуитивно понятным.
И это даст вам дополнительные возможности.
Пожалуйста, посмотрите:
#include <iostream>
#include <map>
#include <string>
#include <any>
#include <utility>
class Caller
{
std::map<int, std::any> selector;
public:
Caller() : selector() {}
Caller(std::initializer_list<std::pair<const int, std::any>> il) : selector(il) {}
template<typename Function>
void add(int key, Functionamp;amp; someFunction) { selector[key] = std::any(someFunction); };
template <typename ... Args>
void call(int key, Args ... args) {
if (selector.find(key) != selector.end()) {
std::any_cast<std::add_pointer_t<void(Args ...)>>(selector[key])(args...);
}
}
};
// Some demo functions
void a(int x) {
std::cout << "at" << x << 'n';
};
void b(int x, int y) {
std::cout << "bt" << x << 't' << y << 'n';
};
void c(int x, int y, std::string z) {
std::cout << "ct" << x << 't' << y << 't' << z << 'n';
};
void d(std::string s, int x, int y, int z) {
std::cout << "dt" << s << 't' << x << 't' << y << 't' << z << 'n';
};
// Definition of our caller map (using initializer list)
Caller caller{
{1, a},
{2, b},
{3, c} };
int main() {
// Some demo calls
caller.call(1, 1);
caller.call(2, 1, 2);
caller.call(3, 1, 2, std::string("3"));
// Adding an additional function
caller.add(4, d);
// And call this as well.
caller.call(4, std::string("ddd"), 1,2,3);
return 0;
}
Комментарии:
1. Кроме того, вы могли бы использовать
std::map<int, std::variant<std::function<void(int, int)>, std::function<void(int, int, std::string)>>>
.
Ответ №2:
Вы можете обернуть в лямбды, такие как
void func(int type, int a, int b) {
std::string str = "hello";
std::map<int, std::function<void(int, int)>> m {
{1, [](int a, int b) { func1(a, b); } },
{2, [str](int a, int b) { func2(a, b, str); } },
...
};
m[type](a, b);
}
Комментарии:
1. Я переформулировал свой вопрос. Я думаю, подходит ли ваш метод для моего случая…
2. Ближе. Но, видите ли, я не хочу вводить так много кодов
func
. Я пытаюсь съехатьstd::map
отсюдаfunc
. Я имею в виду, что я хочу определитьstd::map
«внеfunc
«, чтобы я мог просто писать такие вещи, какm[type](a, b)
«внутриfunc
«.3. Если другого способа нет, я думаю , что мне могут понадобиться два
map
: один-для хранения функций с двумя параметрами, а другой-для хранения функций с тремя параметрами.4. @Yves Я не могу придумать других способов сделать это, мы должны заранее указать подпись во время компиляции. И да, вы можете определить два
map
s, если тип 3-го параметра фиксирован.5. Вы можете написать
dummy_func1(int, int, str)
, что просто игнорируетеstr
вызовы иfunc1(int, int)
с первыми двумя аргументами.