#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)
они заключены в другой класс, который будет иметь эту функцию, которая обновит оба их значения. Я не знаю, достаточно ли четко я это объяснил. @Jarod424. @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;
}
Просто будьте осторожны при приведении, вам нужно знать правильную подпись и возвращаемый тип.