C ++ нужен контейнер для хранения пользовательских функций - PullRequest
0 голосов
/ 13 ноября 2018

Я пытаюсь создать интерфейс между пользовательской функцией и данными. Допустим, мне нужно создать функцию с именем MapFun(), ввод MapFun() включает в себя дескриптор пользовательской функции (UDF) и ввод UDF.

void userFun1(Data data, int in1, int in2){
    // user defined function 1;
}

void userFun2(Data data, int in1, int in2, std::string s){
    // user defined function 2;
}

// ...
// apply user function 1 on data
MapFun(@userFun1, data, 3, 4);
// apply user function 2 on data
MapFun(@userFun2, data, 1, 2, "algorithm");

Пользователь напишет userFun и применит его с MapFun(). Итак, как оформить MapFun()? Пользовательская функция может иметь разные входные данные, и подпись не может быть предсказана. Кроме того, MapFun() не будет оценивать userFun сразу, вместо этого он хранит все userFun и выполняет ленивую оценку.

Любые предложения с благодарностью.

Ответы [ 3 ]

0 голосов
/ 13 ноября 2018

Если я правильно понял, вы просто хотите сохранить функцию и набор аргументов, чтобы они могли быть вызваны позже. Это именно то, что делает std::bind (во всяком случае, самое простое, что он делает). На самом деле, вы могли бы просто использовать std::bind прямо вместо вашего MapFun:

auto func1 = std::bind(userFun1, data, 3, 4);
auto func2 = std::bind(userFun2, data, 1, 2, "algorithm");
// ... and later,
func1();
func2();

Обратите внимание на auto в этих: вы не можете (переносимо) записать тип любого объекта, возвращаемого std::bind, и эти два примера объекта будут иметь разные типы. Но оба из них неявно преобразуются в общий тип std::function<void()>. Вы можете хранить кучу std::function<void()> объектов практически в любом типе контейнера, который вы хотите использовать позже:

std::map<unsigned int, std::function<void()>> func_map;
func_map[0] = std::bind(userFun1, data, 3, 4);
func_map[1] = std::bind(userFun2, data, 1, 2, "algorithm");
// ...
auto func_iter = func_map.find(key);
if (func_iter != func_map.end() && func_iter->second) {
    (func_iter->second)();
}

Конечно, если вы хотите использовать свое имя функции и / или если вам нужен явный общий тип возврата для правильной работы с другими шаблонами или чем-то еще, вы можете просто обернуть std::bind:

template <class F, class... Args>
std::function<void()> MapFun(F&& f, Args&&... args) {
    return std::bind<void>(std::forward<F>(f), std::forward<Args>(args)...);
}

Или, если ваш MapFun должен хранить вызываемый объект внутри некоторого контейнера члена класса или чего-то еще, а не просто создавать и возвращать один (это не ясно), он, конечно, может сделать это с помощью std::bind и затем добавить результат для любого контейнера.

0 голосов
/ 13 ноября 2018

Пользовательская функция может иметь разные входные данные, и подпись не может быть предсказана.

Кажется, что типичные работы для шаблонов с переменными значениями

Кроме того,MapFun() не будет оценивать userFun сразу, вместо этого он хранит все userFun и выполняет ленивую оценку.

Не уверен, что понимаю, но я полагаю, вы можете получить то, что вы хотите, используяstd::bind() или, может быть, лучше, с лямбда-функциями.

Я предлагаю следующую переменную C ++ 14-шаблон MapFun() функцию, которая возвращает лямбда-функцию, которая захватывает как пользовательскую функцию, так и аргумент.Эта функция может быть выполнена позже.

template <typename F, typename ... Args>
auto MapFun (F const & f, Args const & ... args)
 { return [=]{ f(args...); }; }

Ниже приведен полный рабочий пример

#include <iostream>

template <typename F, typename ... Args>
auto MapFun (F const & f, Args const & ... args)
 { return [=]{ f(args...); }; }

void userFun1 (int i1, int i2)
 { std::cout << "uf1, " << i1 << ", " << i2 << std::endl; }

void userFun2 (int i1, int i2, std::string const & s)
 { std::cout << "uf2, " << i1 << ", " << i2 << ", " << s << std::endl; }

int main ()
 {
   auto l1 = MapFun(userFun1, 1, 2);
   auto l2 = MapFun(userFun2, 3, 4, "five");

   l2();
   l1();
 }
0 голосов
/ 13 ноября 2018

Если вам нужно только сделать бэкпорт std::invoke из C ++ 17, то его простейшая форма довольно прямолинейна forward:

template<class F, class... Args> decltype(auto) invoke(F &&f, Args &&...args) {
    return ::std::forward<F>(f)(::std::forward<Args>(args)...);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...