2011-05-17 63 views
8

我使用容器来保存指针列表任何东西:比较的boost ::任何内容

struct Example { 
    std::vector<boost::any> elements; 
} 

插入在这个容器中的元素,我写了几个辅助功能(该struct Example成员):

void add_any(boost::any& a) { 
    elements.push_back(a); 
} 

template<typename T> 
void add_to_list(T& a) { 
    boost::any bany = &a; 
    add_any(bany); 
} 

现在,我只想插入元素时,他们不存在于此容器中。为了做到这一点,我认为我只需要在elements之上调用search以及适当的比较器功能。但是,我不知道如何比较boost::any实例。

我的问题: 知道我的boost::any实例总是包含指向某物的指针;有可能比较两个boost::any值吗?


更新

我感谢你的答案。我也设法做到这一点可能不安全的方式:使用boost::unsafe_any_cast获得void**并比较底层的指针。

目前,这工作正常。不过,我会欣赏你的评论:也许这是一个很大的错误!

#include <boost/any.hpp> 
#include <iostream> 
#include <vector> 
#include <string> 
using namespace std; 

bool any_compare(const boost::any& a1, const boost::any& a2) { 
    cout << "compare " << *boost::unsafe_any_cast<void*>(&a1) 
     << " with: " << *boost::unsafe_any_cast<void*>(&a2); 
    return (*boost::unsafe_any_cast<void*>(&a1)) == 
     (*boost::unsafe_any_cast<void*>(&a2)); 
} 

struct A {}; 

class Example { 
public: 
    Example() : elements(0), 
       m_1(3.14), 
       m_2(42), 
       m_3("hello"), 
       m_4() {}; 
    virtual ~Example() {}; 

    void test_insert() { 
     add_to_list(m_1); 
     add_to_list(m_2); 
     add_to_list(m_3); 
     add_to_list(m_4); 
     add_to_list(m_1); // should not insert 
     add_to_list(m_2); // should not insert 
     add_to_list(m_3); // should not insert 
     add_to_list(m_4); // should not insert 
    }; 

    template <typename T> 
    void add_to_list(T& a) { 
     boost::any bany = &a; 
     add_any(bany); 
    } 

private: 
    vector<boost::any> elements; 
    double m_1; 
    int m_2; 
    string m_3; 
    A  m_4; 


    void add_any(const boost::any& a) { 
     cout << "Trying to insert " << (*boost::unsafe_any_cast<void*>(&a)) << endl; 
     vector<boost::any>::const_iterator it; 
     for (it = elements.begin(); 
      it != elements.end(); 
      ++it) { 
      if (any_compare(a,*it)) { 
       cout << " : not inserting, already in list" << endl; 
       return; 
      } 
      cout << endl; 
     } 
     cout << "Inserting " << (*boost::unsafe_any_cast<void*>(&a)) << endl; 
     elements.push_back(a); 
    }; 


}; 



int main(int argc, char *argv[]) { 

    Example ex; 
    ex.test_insert(); 
    unsigned char c; 
    ex.add_to_list(c); 
    ex.add_to_list(c); // should not insert 

    return 0; 
} 
+2

备注:你的'add_any'和'add_to_list'函数应该以const-reference作为参数。另外,如果你想要唯一性,你可以考虑使用'set'而不是'vector'。当然,你仍然需要比较功能。 – 2011-05-17 09:53:53

+0

好问题。这可能是不可能的。然而,如果你愿意实现*你自己*任何,有办法做到这一点(如果基础类型都相同,但不支持比较语义)*运行时失败* – 2011-05-17 10:23:49

+0

你确定你想将参数的地址存储到'add_to_list'帮助器中?请注意'boost :: any'会复制对象,但在这种情况下,对象是一个指针。 – 2011-05-17 10:59:36

回答

3

唯一的简单的方法来做到这一点,我能想到的中包含了这样你在any实例存储类型,破坏了很多any用处的硬编码支持...

bool equal(const boost::any& lhs, const boost::any& rhs) 
{ 
    if (lhs.type() != rhs.type()) 
     return false; 

    if (lhs.type() == typeid(std::string)) 
     return any_cast<std::string>(lhs) == any_cast<std::string>(rhs); 

    if (lhs.type() == typeid(int)) 
     return any_cast<int>(lhs) == any_cast<int>(rhs); 

    // ... 

    throw std::runtime_error("comparison of any unimplemented for type"); 
} 

使用C++ 11的type_index,您可以使用或std::unordered_map键入std::type_index(some_boost_any_object.type()) - 类似于Alexandre在他的评论中的建议。

+1

'map '应该更具可扩展性。 – 2011-05-17 10:45:05

+0

@Alexandre:虽然...我的记忆不会工作可靠......我的回忆是,你可以有多个type_info对象的相同类型(每翻译单元,不保证被链接器删除),所以你需要比较'使用提供的'operator =='type_info's ...来自'type_info-> name()的偶映射是不可移植的,因为实现允许提供任何值,包括跨类型的空字符串或常量值。 'type_info'非常特别,你一定要喜欢它! – 2011-05-17 10:51:36

+3

@Alexandre:'std :: type_info :: before' – 2011-05-17 11:39:40

4

你不能直接提供的,但实际上你可以使用any作为基本类型......虽然为指针,这是毫无意义(啊!)

struct any { 
    std::type_info const& _info; 
    void* _address; 
}; 

而且一个模板构造函数:

template <typename T> 
any::any(T* t): 
    _info(typeid(*t)), 
    _address(dynamic_cast<void*>(t)) 
{ 
} 

这基本上是boost::any

现在我们需要用我们的比较机制“扩充”它。

为了做到这一点,我们将“捕获”std::less的实施。

typedef bool (*Comparer)(void*,void*); 

template <typename T> 
bool compare(void* lhs, void* rhs) const { 
    return std::less<T>()(*reinterpret_cast<T*>(lhs), *reinterpret_cast<T*>(rhs)); 
} 

template <typename T> 
Comparer make_comparer(T*) { return compare<T>; } 

并扩充构造函数any

​​

然后,我们提供的less(或operator<

bool operator<(any const& lhs, any const& rhs) { 
    if (lhs._info.before(rhs._info)) { return true; } 
    if (rhs._info.before(lhs._info)) { return false; } 
    return (*lhs._comparer)(lhs._address, rhs._address); 
} 

注专业化:封装,等等都作为练习留给读者

1

没有必要创建新的类。尝试使用xany https://sourceforge.net/projects/extendableany/?source=directory xany类允许将新方法添加到任何现有功能。顺便说一下,在文档中有一个例子完全符合您的需求(创建可比较的_any)。

+0

似乎并不 苹果LLVM 5.0版(铛 - 500.2.78)(基于LLVM 3.3svn) 目标上铛编译:x86_64的 - 苹果darwin13.0.0 还有什么是这个代码的许可证? – berkus 2014-01-03 16:57:55

1

如果您可以更改容器中的类型,则有Boost.TypeErasure。它提供了简单的方法来定制any。比如我用这样的typedef相似的目的:

#include <boost/type_erasure/any.hpp> 
#include <boost/type_erasure/operators.hpp> 

using Foo = boost::type_erasure::any< 
    boost::mpl::vector< 
     boost::type_erasure::copy_constructible<>, 
     boost::type_erasure::equality_comparable<>, 
     boost::type_erasure::typeid_<>, 
     boost::type_erasure::relaxed 
    > 
>; 

Foo行为完全一样boost::any,但它可以进行比较,以平等和使用boost::type_erasure::any_cast代替boost::any_cast