2012-05-19 38 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++沒有反射功能。

您必須定義例如將字符串映射到函數指針的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. 
    } 
    } 
}; 

這就是所謂的數據驅動的界面,在這裏你傳遞給對象的命令,他們以他們在一個多態的方式識別命令做出響應。您可以通過創建靜態初始化的無序映射(從命令到函數指針)然後使用它來解析要調用的函數來改進所顯示的內容。如果可以的話,儘量避免使用這種類型的函數調度是件好事,因爲它與靜態函數調度相比較慢並且容易出錯,因爲拼寫錯誤可能導致不正確的調用或錯誤。它還有一個缺點,就是你無法輕易獲得返回值,儘管在某些情況下也是如此。

編輯:我想給這可怎麼做了較爲完整的例子,所以這裏有雲:

#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 

正如你可以看到它是一個相當有點工作來設置它,但添加實現界面的新類型非常簡單。