То, что вы пытаетесь сделать, невозможно без серьезной работы во время выполнения и сопутствующих затрат.Самым простым решением, конечно же, было бы просто сохранить boost :: any (any_function
никогда не превращался в boost) внутри вашей карты и выполнить необходимые приведения (или добавить некоторые данные времени выполнения, которые сообщают вам, какой приём ксделать), хотя вы должны избегать этого любой ценой и идти с фиксированными аргументами или без аргументов.Затем ваши пользователи могут изменять свои функции, используя bind
, чтобы соответствовать требуемой подписи.
Редактировать: В вашей текущей схеме я не вижу причин для CommandManager
хранить Command*
на карте.
Edit2: также вы отбрасываете тип возврата.Это может быть хорошо для вашего варианта использования, но делает это намного менее общим.
Edit3: я разработал несколько рабочих примеров вашего кода, используя any
.Я чувствую, что есть некоторый недостаток, и я действительно не вижу, чего это должно достигнуть, но здесь это идет:
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <boost/any.hpp>
class AnyCaller
{
std::map<std::string, boost::any> calls;
public:
AnyCaller() {}
void add(const std::string& name, const boost::any& fun) {
calls[name] = fun;
}
// we always use the throwing version of any_cast
// icbb by error checking
// no arg version
template<typename Ret>
Ret call(const std::string& s) {
const boost::any& a = calls[s];
return boost::any_cast< std::function<Ret(void)> >(a)();
}
// this should be a variadic template to be actually usable
template<typename Ret, typename T>
Ret call(const std::string& s, T&& arg) {
// we have to assume that our users know what we are actually returning here
const boost::any& a = calls[s];
return boost::any_cast< std::function<Ret(T)> >(a)(std::forward<T>(arg));
}
virtual ~AnyCaller() {}
};
int foo() { std::cout << "foo" << std::endl; return 1; }
double foo2(int i) { std::cout << "foo2" << std::endl; return double(i); }
int main()
{
AnyCaller c;
c.add("foo", std::function<int(void)>(foo));
c.add("foo2", std::function<double(int)>(foo2));
c.call<int>("foo");
c.call<double, int>("foo2", 1);
// this should throw
c.call<double, int>("foo", 1);
return 0;
}
Что касается примера использования фиксированной подписи.Подумайте, что было бы наиболее естественным представлением функции, которую вы собираетесь хранить (глядя на ваш Command
пример, я бы предположил, что это std::function<void(void)>
. Храните функции этого типа и всякий раз, когда один из ваших пользователей пытается их использоватьон должен bind
любую функцию, которую он хочет использовать, поэтому она соответствует этой подписи.