0

これが私が持っているコードです...

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

4 に答える 4

3

できません。C++にはリフレクション機能がありません。

std::mapたとえば、文字列を関数ポインタにマップするaを定義する必要があります。

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);
}
于 2012-05-19T19:46:28.073 に答える
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]";
}
于 2012-05-19T19:50:23.413 に答える
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

ご覧のとおり、これを設定するのはかなりの作業ですが、インターフェースを実装する新しいタイプを追加するのは非常に簡単です。

于 2012-05-19T20:00:26.953 に答える
0

これは基本的に、Java1.5以降で利用可能なリフレクションメカニズムです。

これがC++でのリフレクションの例です

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

于 2012-05-19T19:53:41.847 に答える