2010-08-31 81 views
2

我必须存储一个不同的boost :: function对象的列表。为了提供这个我使用boost :: any。我有几个功能需要不同的功能签名,将它们打包成任意,然后插入到给定类型的特殊映射中。下面是代码:存储提升功能

enum TypeEnumerator 
{ 
    e_int, 
    e_float, 
    e_double 
}; 

typedef map< string, pair<any, TypeEnumerator> > CallbackType; 
CallbackType mCallbacks; 

void Foo(const string &name, function<float()> f) 
{ 
    mCallbacks[name] = make_pair(any(f), CLASS::e_float); 
} 
void Foo(const string &name, function<int()> f) { /* the same, but with e_int */ } 
void Foo(const string &name, function<double()> f) { /* the same, but with e_double */ } 

现在我已经在地图升压功能,包装成与枚举类型给出任何,要认识到它的未来。现在我必须调用给定的函数。从任何铸造将无法正常工作:

BOOST_FOREACH(CallbackType::value_type &row, mCallbacks) 
{ 
    // pair<any, TypeEnumerator> 
    switch (row.second.second) // Swith the TypeEnumerator 
    { 
     case 0: // int 
      any_cast< function<int()> >(row.first)(); 
     break; 
     case 1: // float 
      any_cast< function<float()> >(row.first)(); 
     break; 
     case 2: // double 
      any_cast< function<double()> >(row.first)(); 
     break; 
    } 
} 

运行期间,这将不投,我得到异常:

what(): boost::bad_any_cast: failed conversion using boost::any_cast 

是否可以转换回的boost ::功能对象?

+0

你是否忘记了'case 1'中的'break'? – kennytm 2010-08-31 07:05:09

+0

@KennyTM是的,但它仍然失败。 – Ockonal 2010-08-31 07:09:01

回答

4

@TC为运行时错误提供了解决方案。但我相信你应该使用Boost.Variant而不是Boost.Any,因为它只能存储固定选择的类型。借助Boost.Variant,您可以消除该枚举,因为它已经提供了一个标准的访问者模式界面。 (result):

#include <boost/variant.hpp> 
#include <boost/function.hpp> 
#include <boost/foreach.hpp> 
#include <map> 
#include <string> 
#include <iostream> 

typedef boost::variant<boost::function<int()>, 
         boost::function<float()>, 
         boost::function<double()> > Callback; 
typedef std::map<std::string, Callback> CallbackType; 

CallbackType mCallbacks; 

void Foo(const std::string& name, const Callback& f) { 
    mCallbacks[name] = f; 
} 

//------------------------------------------------------------------------------ 

float f() { 
    std::cout << "f called" << std::endl; 
    return 4; 
} 

int g() { 
    std::cout << "g called" << std::endl; 
    return 5; 
} 

double h() { 
    std::cout << "h called" << std::endl; 
    return 4; 
} 

//------------------------------------------------------------------------------ 

struct call_visitor : public boost::static_visitor<> { 
    template <typename T> 
    void operator() (const T& operand) const { 
     operand(); 
    } 
}; 


int main() { 
    Foo("f", boost::function<float()>(f)); 
    Foo("g", boost::function<int()>(g)); 
    Foo("h", boost::function<double()>(h)); 

    BOOST_FOREACH(CallbackType::value_type &row, mCallbacks) { 
     boost::apply_visitor(call_visitor(), row.second); 
    } 

    return 0; 
} 
+0

+1,好点。 – 2010-08-31 07:38:56

+0

不错的主意,谢谢。 – Ockonal 2010-08-31 08:28:36

+0

@KennyTM hm,如何获取返回值? 'boost :: apply_visitor(call_visitor(),row.second)' – Ockonal 2010-08-31 09:05:05

3

从外观来看,row.first是回调名称,string。你或许应该使用row.second.first

case 0: // int 
    any_cast< function<int()> >(row.second.first)(); 
    break; 

此外,你应该在开关(case CLASS::e_int:),而不是使用幻数你的枚举常量。

+0

>你应该使用你的枚举常量; 为什么? – Ockonal 2010-08-31 07:20:10

+1

@Ockonal:因为它更安全。考虑一下如果你后来决定你也想要一个e_char常量,并且不小心将它添加为你的枚举中的第一个常量,会发生什么。现在所有其他的枚举值都会改变,并且您也必须修复交换机中的魔术数字,或者有一个令人讨厌的错误。 – 2010-08-31 07:25:31