Сохранять функции с различными сигнатурами на карте
Я пытаюсь создать ключ map
с string
в качестве ключа и общий метод как value
в С++, но я не знаю, возможно ли это. Я хотел бы сделать что-то вроде этого:
void foo(int x, int y)
{
//do something
}
void bar(std::string x, int y, int z)
{
//do something
}
void main()
{
std::map<std::string, "Any Method"> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello", 1, 2);
}
Это возможно? Если да, то как я могу это понять?
Ответы
Ответ 1
Вы можете ввести типы стилей в контейнер, а затем предоставить шаблон operator()
. Это вызовет std::bad_any_cast
, если вы ошибетесь.
template<typename Ret>
struct AnyCallable
{
template<typename ... Args>
AnyCallable(std::function<Ret(Args...)> fun) : m_any(fun) {}
template<typename ... Args>
Ret operator()(Args && ... args) { return std::any_cast<std::function<Ret(Args...)>>(m_any)(std::forward<Args...>(args...)); }
std::any m_any;
}
template<>
struct AnyCallable<void>
{
template<typename ... Args>
AnyCallable(std::function<void(Args...)> fun) : m_any(fun) {}
template<typename ... Args>
void operator()(Args && ... args) { std::any_cast<std::function<void(Args...)>>(m_any)(std::forward<Args...>(args...)); }
std::any m_any;
}
void foo(int x, int y)
{
//do something
}
void bar(std::string x, int y, int z)
{
//do something
}
void main()
{
std::map<std::string, AnyCallable<void>> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello", 1, 2);
}
Ответ 2
В большинстве случаев (я не могу сказать лучше всего) вы можете использовать стирание подписи. Это означает преобразовать указатель на функции в общий тип подписи и затем преобразовать их обратно в правильную подпись перед их использованием.
Это можно сделать только в особых случаях (я не могу представить себе реальный мир) и будет очень небезопасным: ничто не мешает вам передать неправильные параметры функции. Короче: НИКОГДА НЕ ДЕЛАЙТЕ ЭТО В РЕАЛЬНОМ КОДЕ МИРА.
Как говорится, вот рабочий пример:
#include <iostream>
#include <string>
#include <map>
typedef void (*voidfunc)();
void foo(int x, int y)
{
std::cout << "foo " << x << " " << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar " << x << " " << y << " " << z << std::endl;
}
int main()
{
std::map<std::string, voidfunc> m;
m["foo"] = (voidfunc) &foo;
m["bar"] = (voidfunc)& bar;
((void(*)(int, int)) m["foo"])(1, 2);
((void(*)(std::string, int, int)) m["bar"])("baz", 1, 2);
return 0;
}
Он дает как ожидалось:
foo 1 2
bar baz 1 2
Я не мог найти в стандарте, вызывает ли это вызов или нет Undefined Behavior, потому что мало говорится о преобразованиях указателей функций, но я уверен, что все распространенные компиляторы принимают это, потому что он включает в себя только кавычки указателей функций.
Ответ 3
Вы не можете хранить функции с разными сигнатурами в контейнере типа map
, независимо от того, сохраните ли вы их как указатель на функцию или std ::function<WHATEVER>
. Информация о сигнатуре функции является одной и единственной в обоих случаях.
Типы для value
in map
- это один, что означает, что сохраненный в нем объект все одного и того же типа.
Итак, если ваши функции имеют одинаковую подпись, тогда это легко, иначе вам придется отказаться от безопасности типов и начать ходить в очень опасном мире.
Тот, в котором вы стираете информацию о типе о функциях, хранящихся внутри карты.
Это означает что-то вроде map<string, void*>
.