Как обернуть функцию с различным числом аргументов по умолчанию, чтобы иметь только один аргумент?

У меня есть функция шаблона, назовите ее "клиентом":

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<>().

То, что я хотел бы сделать, - автоматизировать упаковку или иметь одну (шаблонизированную) упаковку вместо одного адаптера. Я чувствую, что это может иметь место для вариаторов, но у них мало идеи о том, как их применять.

С++ 11 в порядке, С++ 14 прекрасно, если это абсолютно необходимо.

Ответы

Ответ 1

С++ 11 в порядке, С++ 14 прекрасно, если это абсолютно необходимо.

С++ 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");
}

Ответ 2

Это один из тех случаев, когда макрос помогает:

#define WRAP_FN(f) +[](std::string const& s) -> decltype(auto) { return f(s); }

Хотя вы могли бы просто написать тело этого встроенного текста.


Нет ничего другого, что вы могли бы сделать. Проблема в том, что аргументы по умолчанию не видны в сигнатуре функции, поэтому, как только вы попадаете в систему типов, нет возможности различать:

void works(std::string const&, int=0);
void fails(std::string const&, int  );

Оба из них void(*)(std::string const&, int). Таким образом, у вас не может быть шаблона функции или оболочки шаблона шаблона - вам нужно сделать это с помощью lambda (или макроса, который обертывает лямбда).

Ответ 3

Я думаю, что создам класс, который обертывает ваши параметры, и клиент примет экземпляр этого класса. Таким образом, у вас есть только один параметр, который содержит сколько угодно параметров.

Эта оболочка параметров также предоставит значения по умолчанию и позволит вам уточнить их в производных классах для определенных целей.

Это, вероятно, немного больше самодокументируется, когда по сравнению с лямбдами.

И кто знает, когда его время читать и записывать параметры из файла, класс оболочки был бы идеальным местом для этого.