你可以做这样的事情,但由于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
由于你可以看到,建立这一接口的工作范围很广,但增加实施接口的新类型非常容易。