2012-05-19 11 views
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; 
} 

Возможно ли это?

+0

Нет, вам нужно сказать '#define method foo', чтобы сделать эту работу. Макросы являются частью препроцессора, который запускает * перед * компилятор даже запущен. –

ответ

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]"; 
} 
+0

Это как-то решит мою проблему, но я получаю некоторые ошибки, которые я не понимаю при вызове CALL_MF. –

+0

@ JanTuroň: Попробуйте прямо без макроса, как указано в комментарии. –

3

Вы не можете. C++ не имеет возможностей отражения.

Вам нужно будет определить, например, a 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); 
} 
+0

Ну, я могу создать такую ​​карту во время выполнения? –

+0

@ JanTuroň: Да. –

+0

Отлично. Не могли бы вы показать мне, как? –

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. 
    } 
    } 
}; 

Это называется интерфейс управляемых данных, где вы передавать команды на объекты, и они реагируют на команды, которые они распознают в полиморфной форме. Вы можете улучшить то, что я показал, создав статически инициализированную неупорядоченную карту от команд до указателя функции, а затем используя это, чтобы разрешить, какую функцию вызывать. Это хорошо, если вы можете избежать этого типа отправки функций, потому что он медленный по сравнению с отправкой статической функции и ошибкой, поскольку опечатки могут приводить к неправильным вызовам или ошибкам. Это также имеет недостаток, что вы не можете легко получить возвращаемое значение, хотя это возможно в некоторых случаях.

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 

Как вы можете видеть, что это довольно бит работы по настройке, но добавление новых типов, реализующих интерфейс, довольно просто.

Смежные вопросы