如何在变量中传递方法名?

时间:2012-05-19 19:44:35

标签: c++ reflection

这是我的代码......

struct Test {
  string foo() { return "bar"; }
};

#define callFn(obj,method) obj->method();

int main() {
  Test* t = new Test();
  cout << callFn(t,foo); // bar
  return 0;
}

......这是我想要的代码

int main() {
  Test* t = new Test();
  string method = "foo";
  cout << callFn(t,method); // bar
  return 0;
}

有可能吗?

4 个答案:

答案 0 :(得分:3)

你做不到。 C ++没有反射功能。

您必须定义,例如一个std::map,它将字符串映射到函数指针。

void foo(int x) { std::cout << "foo " << (x+3) << "\n"; }
void bar(int x) { std::cout << "bar " << (x+5) << "\n"; }

int main() {
    std::map<std::string, void (*)(int)> mapper;
    mapper["foo"] = &foo;
    mapper["bar"] = &bar;

    // ...

    mapper["foo"](42);
    mapper["bar"](42);
}

答案 1 :(得分:1)

您可能想要成员函数指针之类的东西:

typedef std::string (Test::*ptmf)();

#define CALL_MF(pobject, p) (((pobject)->*(p))())

int main()
{
    ptmf method = &Test::foo;
    Test * t = new Test;
    std::string result = CALL_MF(t, method);   // or directly: (t->*method)()
}

您可以创建其元素类型为ptmf的容器,以便在运行时管理不同的成员函数指针:

std::map<int, ptmf> function_registry;

std::string call(int key, Test * t)
{
    auto it = function_registry.find(key);
    return (it != function_registry.end()) ? CALL_MF(t, *it) : "[ERROR]";
}

答案 2 :(得分:1)

你可以做这样的事情,但由于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

正如您所看到的,设置它有很多工作,但添加实现该接口的新类型非常容易。

答案 3 :(得分:0)

这实际上是后Java1.5中可用的反射机制

以下是C ++中反射的一个例子

http://www.garret.ru/cppreflection/docs/reflect.html