Как обернуть функцию переменным числом аргументов по умолчанию, чтобы иметь только один аргумент?
У меня есть функция шаблона, давайте назовем ее "клиент":
template<typename T>
void client(T (*func)(const std::string&), const std::string& s) {}
Кроме того, существует ряд "адаптируемых" функций, которые имеют идентичный тип первого аргумента, отличного от значения по умолчанию, но следующие аргументы различаются по количеству и имеют значения по умолчанию:
void adaptee_one(const std::string&, int i = 1, char* c = nullptr) {}
void adaptee_two(const std::string&, float* f = nullptr) {}
Вышеуказанные функции даны. Теперь я хочу передать их вышеупомянутым client<>()
функция в качестве первого параметра, и я забочусь только о передаче первого аргумента, const std::string&
, Поэтому я делаю следующее:
void bindAdapteeOne(const std::string& s) {
return adaptee_one(s);
}
void bindAdapteeTwo(const std::string& s) {
return adaptee_two(s);
}
А потом пройти bindAdapteeX()
в client<>()
,
То, что я хотел бы сделать, это автоматизировать упаковку или иметь одну (шаблонную) оболочку вместо одной для каждого адаптера. Я чувствую, что это может быть в случае с вариадиками, но я не знаю, как их точно применять.
C++ 11 в порядке, C++14 в порядке, если это абсолютно необходимо.
3 ответа
C++11 в порядке, C++14 в порядке, если это абсолютно необходимо.
C++11 решение здесь.
То, что я хотел бы сделать, это автоматизировать упаковку или иметь одну (шаблонную) оболочку вместо одной для каждого адаптера.
Я бы не стал этого делать. Вы можете просто использовать не захватывающие лямбды и позволить им распадаться, чтобы функционировать указатели:
client (+[](const std::string& s) { return adaptee_one(s); }, "foo");
Я не думаю, что оборачивая их в шаблоны или что-то еще, вы получите решение, которое будет более читабельным или простым в использовании.
Как минимальный рабочий пример:
#include<string>
template<typename T>
void client(T (*func)(const std::string&), const std::string& s) {}
void adaptee_one(const std::string&, int i = 1, char* c = nullptr) {}
void adaptee_two(const std::string&, float* f = nullptr) {}
int main() {
client (+[](const std::string& s) { return adaptee_one(s); }, "foo");
}
Это один из тех случаев, когда макрос помогает:
#define WRAP_FN(f) +[](std::string const& s) -> decltype(auto) { return f(s); }
Хотя вы могли бы просто написать тело этого inline вместо этого.
Больше ты ничего не сможешь сделать. Проблема в том, что аргументы по умолчанию не видны в сигнатуре функции, поэтому, как только вы попадете в систему типов, нет никакой возможности провести различие:
void works(std::string const&, int=0);
void fails(std::string const&, int );
Оба из них void(*)(std::string const&, int)
, Таким образом, у вас не может быть шаблона функции или обертки шаблона класса - вам нужно сделать это inline с лямбда-выражением (или макросом, который оборачивает лямбда-выражение).
Я думаю, я бы создал класс, который обернет ваши параметры, и пусть клиент примет экземпляр этого класса. Таким образом, у вас есть только один параметр, который содержит столько параметров, сколько вы пожелаете.
Этот упаковщик параметров также предоставит значения по умолчанию и позволит вам уточнить их в производных классах для конкретных целей.
Это, вероятно, немного более самодокументирующееся по сравнению с лямбдами.
И кто знает, когда пора читать и записывать параметры из файла, тогда класс-обертка будет идеальным местом для этого.