Создание неупорядоченной карты std::функций с любыми аргументами?

#c #c 11 #unordered-map

#c #c 11 #неупорядоченная карта

Вопрос:

Я пытаюсь создать неупорядоченную карту 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;

}
  

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

 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 , я получаю ошибку компилятора.

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

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

Комментарии:

1. Как вы собираетесь это использовать? Откуда вы знаете, что myMap[name] должно вызываться с нулем, одним или несколькими параметрами?

2. Вы можете использовать std::unordered_map<std::string, std::any> .

3. Я думаю, я бы знал, какую функцию нужно вызвать. Я пытаюсь найти слова, чтобы правильно объяснить себя. Допустим, у меня есть функция, которую необходимо обновить в классе и пользовательском интерфейсе. И у обоих из них есть вызываемая функция updateValue(int) . Вместо вызова ui->updateValue(int) и class->updateValue(int) они заключены в другой класс, который будет иметь эту функцию, которая обновит оба их значения. Я не знаю, достаточно ли четко я это объяснил. @Jarod42

4. @Sailanarmo пример: coliru.stacked-crooked.com/a/b1415747073e8ba9

5. @Sailanarmo audo mapIter = myMap.find("doMaths"); int result = std::any_cast <int (*) (int)> (mapIter->second) (42); , смотри: wandbox.org/permlink/dOMqAhQeuv6d5fuC

Ответ №1:

Как говорит 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 << resu<
}
  

Живая демонстрация

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

Пожалуйста, обратите внимание: std::any требуется C 17, см.:https://en.cppreference.com/w/cpp/utility/any

Комментарии:

1. Большое вам спасибо! Это мне очень поможет.

2. @Sailanarmo С удовольствием

Ответ №2:

Если вы знаете, что тип вашей функции 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)>> и посмотрите его там.

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

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

Комментарии:

1. @nm Если вы прочитали комментарии OP выше, я думаю, он рад «узнать» сигнатуру функции для функции, которую он планирует вызвать после извлечения ее с карты (хорошо это или плохо).

Ответ №3:

Как сказал Пол, но с 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;
}
  

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