Создание unordered_map для std :: functions с любыми аргументами? - PullRequest
2 голосов
/ 04 апреля 2019

Я пытаюсь создать неупорядоченную карту std::functions.Где ключ - это строка, где вы будете искать функцию, которую хотите вызвать, а функция - это значение.

Я написал небольшую программу:

#include <iostream>
#include <unordered_map>
#include <functional>
#include <string>

void func1()
{
  std::cout << "Function1 has been called." << std::endl;
}

int doMaths(int a)
{
  return a + 10;
}

int main()
{
  std::unordered_map<std::string,std::function<void()>> myMap;

  myMap["func1"] = func1;

}

Это прекрасно компилируется, и я могу вызвать функцию (однако я не уверен, что это правильный способ сделать это)place:

auto mapIter = myMap.find("func1");
auto mapVal = mapIter->second;
mapVal();

Затем вызывается функция, однако я думаю, что это за счет создания новой копии функции?Пожалуйста, поправьте меня, если я ошибаюсь по этому поводу.

Однако, если я пытаюсь сделать: myMap["doMaths"] = doMaths; Я получаю ошибку компилятора, так как значение в myMap равно std::function<void()>>, а не std::function<int(int)>>.Я получил это для компиляции, когда я сделал: myMap["doMaths"] = std::bind(doMaths,int()); Однако я не знаю, что это на самом деле делает.И когда я пытаюсь вызвать его так же, как func1, я получаю ошибку компилятора.

Так что я думаю, у меня есть два вопроса:

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

Ответы [ 3 ]

4 голосов
/ 04 апреля 2019

Как говорит eerorika, вы можете сделать это с картой std::any. Вот пример кода, показывающий также, как вы вызываете указатели функций, хранящиеся на карте:

#include <iostream>
#include <unordered_map>
#include <string>
#include <any>

void func1()
{
    std::cout << "Function1 has been called.\n";
}

int doMaths(int a)
{
    std::cout << "doMaths has been called, a = " << a << "\n";
    return a + 10;
}

int main()
{
    std::unordered_map<std::string,std::any> myMap;

    myMap["func1"] = func1;
    auto mapIter = myMap.find("func1");
    std::any_cast <void (*) ()> (mapIter->second) ();

    myMap["doMaths"] = doMaths;
    mapIter = myMap.find("doMaths");
    int result = std::any_cast <int (*) (int)> (mapIter->second) (5);
    std::cout << result;
}

Живая демоверсия

std:any_cast вызовет исключение std::bad_any_cast во время выполнения, если типы (или, в данном случае, сигнатуры функций) не совпадают.

Обратите внимание: std::any требуется C ++ 17, см .: https://en.cppreference.com/w/cpp/utility/any

1 голос
/ 04 апреля 2019

Как сказал Пол, но с синтаксисом std::function.Вы можете использовать это для размещения функций с любой подписью на вашей карте, даже лямбда-выражениями: D

#include <vector>
#include <iostream>
#include <functional>
#include <map>
#include <any>

int doMaths(int a)
{
    std::cout << "doMaths has been called, a = " << a << "\n";
    return a + 10;
}

int main()
{
  std::map<std::string, std::any> map;

  map["foo"] = std::function<int(int)>([](int a) { return a * 2; });

  map["bar"] = std::function<int(int, int)>([](int a, int b) { return a + b; });

  map["maths"] = std::function<int(int)>(doMaths);

  int a = std::any_cast<std::function<int(int)>>(map["foo"])(4);

  int b = std::any_cast<std::function<int(int, int)>>(map["bar"])(4, 5);

  int c = std::any_cast<std::function<int(int)>>(map["maths"])(5);

  std::cout << a << " " << b << " " << c <<std::endl;
}

Просто будьте осторожны, когда вы разыгрываете, вам нужно знать правильную подпись и тип возвращаемого значения.

1 голос
/ 04 апреля 2019

Если вы знаете, что тип вашей функции std::function<void(void)>, поместите ее в std::unordered_map<std::string,std::function<void()>> и посмотрите ее там.

Если вы знаете, что тип вашей функции std::function<int(double, char)>, поместите это в std::unordered_map<std::string,std::function<int(double, char)>>, и посмотрите это там.

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

Если у вас более одного типа функцийТакже есть более одной карты.Шаблон переменной (или шаблон функции со статической переменной карты) может помочь с любым количеством таких карт без дублирования кода.Конечно, таким образом, вы можете иметь только глобальную карту.Класс, который может управлять такой коллекцией карт, может быть немного более сложным, но только немного.

...