你可以做這樣的事情,但因爲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.
}
}
};
這就是所謂的數據驅動的界面,在這裏你傳遞給對象的命令,他們以他們在一個多態的方式識別命令做出響應。您可以通過創建靜態初始化的無序映射(從命令到函數指針)然後使用它來解析要調用的函數來改進所顯示的內容。如果可以的話,儘量避免使用這種類型的函數調度是件好事,因爲它與靜態函數調度相比較慢並且容易出錯,因爲拼寫錯誤可能導致不正確的調用或錯誤。它還有一個缺點,就是你無法輕易獲得返回值,儘管在某些情況下也是如此。
編輯:我想給這可怎麼做了較爲完整的例子,所以這裏有雲:
#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'來使這個工作。宏是預處理器的一部分,在編譯器啓動之前運行。 –