Вы можете сделать что-то вроде этого, а потому, что C++ не хватает возможности отражения вы должны сделать некоторую дополнительную работу, чтобы сделать это возможным.
struct base {
virtual void call_method(std::string const &) = 0;
};
struct derived : public base {
std::string foo() const {
return "bar";
}
// More methods.
void call_method(std::string const &p_name) {
if(p_name == "foo") {
this -> foo();
}
// More checks on method names.
else {
// Handle invalid function name.
}
}
};
Это называется интерфейс управляемых данных, где вы передавать команды на объекты, и они реагируют на команды, которые они распознают в полиморфной форме. Вы можете улучшить то, что я показал, создав статически инициализированную неупорядоченную карту от команд до указателя функции, а затем используя это, чтобы разрешить, какую функцию вызывать. Это хорошо, если вы можете избежать этого типа отправки функций, потому что он медленный по сравнению с отправкой статической функции и ошибкой, поскольку опечатки могут приводить к неправильным вызовам или ошибкам. Это также имеет недостаток, что вы не можете легко получить возвращаемое значение, хотя это возможно в некоторых случаях.
EDIT: Я хотел бы дать более полный пример того, как это можно сделать, так что здесь идет:
#include <cassert>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/blank.hpp>
#include <boost/variant.hpp>
#include <boost/function.hpp>
#include <boost/unordered_map.hpp>
#include <boost/assign/list_of.hpp>
// A base class that defines an interface to call methods by name
// and to access the list of methods. We use a map of argument
// names to boost::variants to pass arguments to the functions.
// Right now we support only ints and strings, but we can expand
// this to other types if we want. In particular, we can use
// boost::any to support arbitrary types, but it will be slow.
// Maybe that's not a big deal since function dispatch through
// named functions is slow anyway.
struct base {
typedef boost::variant< boost::blank, int, std::string > argument_t;
typedef boost::variant< boost::blank, int, std::string > return_t;
typedef boost::unordered_map< std::string, argument_t > param_map_t;
typedef boost::function< return_t (base *, param_map_t const &) >
method_t;
typedef boost::unordered_map< std::string, method_t > method_map_t;
return_t call_method(
std::string const &p_method
, param_map_t const &p_params = param_map_t()
)
{
method_map_t::const_iterator l_itr =
get_methods().find(p_method);
if(l_itr == get_methods().end()) {
// Handle undefined method identifier.
}
return l_itr -> second(this, p_params);
}
virtual method_map_t const &get_methods() const = 0;
};
// A trampoline object to elide the concrete type that
// implements the base interface and to provide appropriate
// casting. This is necessary to force all functions in our
// method map to have the same type.
template< typename U >
base::return_t trampoline(
base::return_t (U::*p_fun)(base::param_map_t const &)
, base *p_obj
, base::param_map_t const &p_param_map
)
{
U *l_obj = static_cast< U* >(p_obj);
return (l_obj ->* p_fun)(p_param_map);
}
// A derived type that implements the base interface and
// provides a couple functions that we can call by name.
struct derived : public base {
static method_map_t const c_method_map;
return_t foo(param_map_t const &p_params) {
std::cout << "foo" << std::endl; return 1;
}
return_t bar(param_map_t const &p_params) {
std::cout << "bar" << std::endl; return std::string("bar");
}
method_map_t const &get_methods() const {
return c_method_map;
}
};
// Construct map of method names to method pointers for derived.
base::method_map_t const derived::c_method_map = boost::assign::map_list_of
("foo", boost::bind(&trampoline<derived>, &derived::foo, _1, _2))
("bar", boost::bind(&trampoline<derived>, &derived::bar, _1, _2))
;
int main() {
base *blah = new derived();
// Call methods by name and extract return values.
assert(boost::get< int >(blah -> call_method("foo")) == 1 );
assert(boost::get<std::string>(blah -> call_method("bar")) == "bar");
// Iterate over available methods
typedef base::method_map_t::const_iterator iterator;
iterator l_itr = blah -> get_methods().begin();
iterator l_end = blah -> get_methods().end ();
for(; l_itr != l_end; ++l_itr) {
if(l_itr -> first == "foo") l_itr -> second(blah, base::param_map_t());
}
}
Выход:
foo
bar
foo
Как вы можете видеть, что это довольно бит работы по настройке, но добавление новых типов, реализующих интерфейс, довольно просто.
Нет, вам нужно сказать '#define method foo', чтобы сделать эту работу. Макросы являются частью препроцессора, который запускает * перед * компилятор даже запущен. –