【问题标题】:Trying to learn boost::intrusive Q2尝试学习 boost::intrusive Q2
【发布时间】:2014-11-12 07:31:42
【问题描述】:

如果我取消注释这些

//BaseList   baselist; 
//MemberList memberlist;

在循环之外并注释掉循环内它崩溃的那些。我需要能够在任何循环之外拥有基本列表(和成员列表)。这是如何实现的?

编辑

我试图以最简单的形式解决的实际问题是这样的。

我想要一个 MyClass 的 std::vector,称之为 AllThingsBunchedTogether。 我还想要一个BaseList 的std::vector,称之为AllThingsSpreadOut。

所以

  • AllThingsBunchedTogether 可能包含(为简洁起见,仅包含 anInt1 部分):1,2,1,10,2,3,4,4,5,9,10,10
  • AllThingsSpreadOut 可能在 [1] 1,1 在 [2] 2,2 在 [3] 3 在 [4] 4,4 在 [5] 5 在 [5] 包含(零暂时未使用) 9]9 在 [10]10,10,10

请注意,数字本身并不存储在 BaseList 中,而是例如 MyClass(1, "John")。

在 [1] 可能是“Mike”、“John”,在 [2] 可能是“Mike”、“Dagobart”在 [3] "John" ... at [10] "John" "Mike" "Dagobart" 等,因此没有重复 AllThingsSpreadOut[i] 中的任何 BaseList,因为每个 MyClass 在每个 BaseList 散列到不同的值 (anInt1 + Name)。

本质上,anInt1 告诉 MyClass 在 AllThingsSpreadOut 中的位置,但 anInt1 + name 保证每个 BaseList 内的唯一性。

所以想法是 AllThingsSpreadOut 是BaseList 的向量,其中向量位置的每个BaseList 是相似事物的列表。

然后,当我从 AllThingsBunchedTogether 中删除内容时(不是通过清除,而是通过搜索以删除某些项目,例如 IsMarkedToDelete 下面的代码),它们将自动从相应的 AllThingsSpreadOut 中消失。

AllThingsSpreadOut 作为 AllThingsBunchedTogether 的一种,具有侵入性语义。 AllThingsBunchedTogether 允许通过 [] 进行超快速访问。

结束编辑

#include <vector>
#include <iostream>

#include <boost/intrusive/list.hpp>

using namespace boost::intrusive;

class MyClass : public list_base_hook<link_mode<auto_unlink>> // This is a derivation hook
{
public:
    std::string name;
    bool bIsMarkedToDelete;
    int anInt1;
public:
    list_member_hook<link_mode<auto_unlink>> member_hook_; // This is a member hook

    MyClass(std::string n, int i) : name(n), anInt1(i), bIsMarkedToDelete(false) {}
};

bool IsMarkedToDelete(const MyClass &o)
{
    return o.bIsMarkedToDelete;
}

//Define a list that will store MyClass using the public base hook
typedef list<MyClass, constant_time_size<false>> BaseList;

// Define a list that will store MyClass using the public member hook
typedef list<MyClass,
        member_hook<MyClass, list_member_hook<link_mode<auto_unlink>>, &MyClass::member_hook_>,
        constant_time_size<false> > MemberList;

int main()
{
    bool done = false;
    std::vector<MyClass> values;

    std::string names[] = {"John", "Mike", "Dagobart"};

    //BaseList   baselist; 
    //MemberList memberlist;

    int i = 0;
    while(!done)
    {
        // Create several MyClass objects, each one with a different value

        for (int j = 0; j < 11; ++j)
            values.emplace_back(names[j % 3], j);

        BaseList   baselist;
        MemberList memberlist;

        // Now insert them in t-he reverse order in the base hook list
        for (auto& e : values)
        {
            baselist.push_front(e);
            memberlist.push_back(e);
        }

        // Now test lists
        auto rbit(baselist.rbegin());
        auto mit(memberlist.begin());
        auto it(values.begin()), itend(values.end());

        // Test the objects inserted in the base hook list
        for (; it != itend; ++it, ++rbit)
        {
            if (&*rbit != &*it)
                return 1;
        }
        // Test the objects inserted in the member hook list
        for (it = values.begin(); it != itend; ++it, ++mit)
        {
            if (&*mit != &*it)
                return 1;
        }
# if 0
        for(auto& e : values)
            std::cout << e.anInt1 << "\n";

        for(auto& e : baselist)
            std::cout << e.anInt1 << "\n";

        for(auto& e : memberlist)
            std::cout << e.anInt1 << "\n";

#endif // 0

        if(2 == i)
        {
            for(auto& e: values)
                std::cout << e.name << "\n";

            for(auto& e: values)
            {
                if("Mike" == e.name)
                    e.bIsMarkedToDelete = true;
            }

            values.erase(
                std::remove_if(values.begin(), values.end(), IsMarkedToDelete), values.end());
        }


        if(i++ > 3)
        {
            values.clear();
            done = true;
        }

        std::cout << "\n";
        std::cout << values.size()     << "\n";
        std::cout << baselist.size()   << "\n";
        std::cout << memberlist.size() << "\n";
    }
}

【问题讨论】:

  • 我无法弄清楚你想在这里实现什么。鉴于示例代码,我看不出任何有意义的“解决方案”。你能告诉我们你想要实现什么(除了人为的目标,比如“我需要能够让基础列表(和成员列表)在任何循环之外”)
  • sehe,我要解决的实际问题比这要复杂得多,以至于我不得不考虑如何以简单的方式提出问题。给我一点时间来改写它。
  • 干杯。思考总是解决方案(“如何通过在 Stackoverflow 上几乎询问问题来解决问题”)
  • 参见上面的编辑。希望这是有道理的。相信我,我已经为这个问题奋斗了好几天。我觉得自己很愚蠢,但我知道问题在于我对 Intrusive 的不熟悉。你现在明白为什么我不能把 Baselist 放在一个循环中了吗?
  • 我正在研究这个问题。问题可能是我使用对象而不是指向对象的指针...

标签: c++ c++11 boost intrusive-containers


【解决方案1】:

我已经看到很晚了,但无论如何,这里是:

  1. 您描述的内容完全匹配MyClass 元素的侵入式哈希表的实现,其中

    • anInt1 是元素的哈希(bucket 标识符)
    • 存储桶列表作为链表实现
    • 平等被定义为(anInt1, Name)的平等


    所以真的,你的程序可能只是是:

    Live On Coliru

    std::unordered_set<MyClass> values {
        { "John",      0 }, { "Mike",      1 }, { "Dagobart",  2 },
        { "John",      3 }, { "Mike",      4 }, { "Dagobart",  5 },
        { "John",      6 }, { "Mike",      7 }, { "Dagobart",  8 },
        { "John",      9 }, { "Mike",     10 },
    };
    
    for(int i = 0; i<=3; ++i) {
        if(2 == i) {
            for(auto& e: values) std::cout << e.name << " "; std::cout << "\n";
            for(auto& e: values) e.bIsMarkedToDelete |= ("Mike" == e.name);
    
            for(auto it=begin(values); it!=end(values);) {
                if (it->bIsMarkedToDelete) it = values.erase(it);
                else ++it;
            }
        }
    
        std::cout << "i=" << i << ", values.size(): " << values.size() << "\n";
    }
    values.clear();
    std::cout << "Done\n";
    
  2. 如果你真的想要连续存储,我只能假设你想要这个以提高性能

    • 想要使用指针而不是对象,因为这只会抵消内存布局(“AllThingsBunchedTogether”)的好处,您最好使用unordered_setunodered_map同上

    • 想使用auto_unlink 模式,因为它会削弱性能(通过执行不受控制的删除触发器、通过抑制恒定时间size() 以及通过创建线程安全问题)

    • 相反,您应该采用上述策略,但使用boost::intrusive::unordered_set 代替请参阅http://www.boost.org/doc/libs/1_57_0/doc/html/intrusive/unordered_set_unordered_multiset.html

      这里又是一个概念验证:

      Live On Coliru

      #include <vector>
      #include <iostream>
      #include <boost/intrusive/unordered_set.hpp>
      #include <vector>
      //#include <functional>
      //#include <algorithm>
      
      namespace bic = boost::intrusive;
      
      struct MyClass : bic::unordered_set_base_hook<bic::link_mode<bic::auto_unlink>>
      {
          std::string name;
          int anInt1;
          mutable bool bIsMarkedToDelete;
      
          MyClass(std::string name, int i) : name(name), anInt1(i), bIsMarkedToDelete(false) {}
      
          bool operator==(MyClass const& o) const { return anInt1 == o.anInt1 && name == o.name; }
      
          struct hasher { size_t operator()(MyClass const& o) const { return o.anInt1; } };
      };
      
      typedef bic::unordered_set<MyClass, bic::hash<MyClass::hasher>, bic::constant_time_size<false> > HashTable;
      
      int main() {
      
          std::vector<MyClass> values {
              MyClass { "John", 0 }, MyClass { "Mike",  1 }, MyClass { "Dagobart", 2 },
              MyClass { "John", 3 }, MyClass { "Mike",  4 }, MyClass { "Dagobart", 5 },
              MyClass { "John", 6 }, MyClass { "Mike",  7 }, MyClass { "Dagobart", 8 },
              MyClass { "John", 9 }, MyClass { "Mike", 10 },
          }; 
      
          HashTable::bucket_type buckets[100];
          HashTable hashtable(values.begin(), values.end(), HashTable::bucket_traits(buckets, 100)); 
      
          for(int i = 0; i<=3; ++i) {
              if(2 == i) {
                  for(auto& e: values) std::cout << e.name << " "; std::cout << "\n";
                  for(auto& e: values) e.bIsMarkedToDelete |= ("Mike" == e.name);
      
                  values.erase(std::remove_if(begin(values), end(values), std::mem_fn(&MyClass::bIsMarkedToDelete)));
              }
      
              std::cout << "i=" << i << ", values.size():    " << values.size()    << "\n";
              std::cout << "i=" << i << ", hashtable.size(): " << hashtable.size() << "\n";
          }
          values.clear();
          std::cout << "Done\n";
      }
      

【讨论】:

  • 添加了标准哈希表和 Boost Intrusive 版本的 coliru 样本(仍然有 auto_unlink 模式节点,尽管我认为已经说过我认为这不是你想要的)
  • sehe,在我看来,您的回复永远不会太晚。我从你的回答中学到了很多。请随时进行教育。再次感谢。
  • 顺便说一句,我将对您最初的建议(我实施并且很快)和这个新想法进行性能比较。
  • 这是否需要 boost_1_57 或更高版本?
  • 不。我用 1_56 编译
【解决方案2】:

这是您省略的错误消息:

Assertion `node_algorithms::inited(to_insert)' failed.

由此我们可以了解到一个元素被插入了两次。这通常不适用于侵入式容器。

当您将列表放入循环中时,它们每次都会被销毁并重新创建。但是当他们在外面时,你永远不会清除它们,你也永远不会清除values,所以出现了这个顺序:

  1. values添加11个元素。
  2. 将所有values 添加到列表中。
  3. values添加11个元素;它仍然有之前的 11 个元素,所以现在有 22 个元素。
  4. 将所有values 添加到列表中。第一个崩溃,因为它已经在列表中。

一种解决方案是在while(!done) 循环的顶部添加values.clear()

【讨论】:

  • 关键是,如果没问题,那么他也不会在循环之外“要求”baseList/memberList。清除向量(显然)也必然会清除列表(如果他们没有使用auto_unlink 钩子,它只会让他们悬空)
  • 我解决了。我需要使用指针。谢谢约翰和塞赫。现在我需要弄清楚如何对这些东西进行单元测试。
  • 另外,我忘记了擦除会使迭代器无效。
  • @Ivan 如果您仍然感兴趣,我刚刚看到您的编辑并在回答中处理了它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-27
  • 1970-01-01
相关资源
最近更新 更多