【问题标题】:MultiIndex Container equal_range .. get output in reverse orderMultiIndex Container equal_range .. 以相反的顺序获取输出
【发布时间】:2018-06-24 18:46:55
【问题描述】:

我有一个 boost::multiIndex 容器。 说:

typedef boost::multi_index<....
 > OrderSet;


OrderSet orderSet_;

int main()
{
    const auto it = orderSet_.get<0>().equal_range(boost::make_tuple(/* Some Values*/))
 if(it.first != it.second)
  while(it.second != it.first) { /* Somethings to do */; 
  --it.second;          
 }
}

程序崩溃并出现offset_ptr 错误。 对于程序it 具有增加订单的价值。所以我首先想要最高价值,然后等等.. 是否是boost::equal_range(..., [&amp;](const auto a, const auto b)-&gt; decltype bool{return a &gt; b;}); 的重载函数 像这样的东西。就像reverse_iterator 中的equal_range()

更新:一个生产者和多个消费者访问多索引容器。当消费者找到equal_range时,生产者添加或删除元素并且消费者抛出错误......这只发生在我反向迭代时......

【问题讨论】:

标签: c++11 boost


【解决方案1】:

更进一步,既然您说offset_ptr,您可能正在使用 Boost Interprocess 分配器。

这是一个演示,展示了如何在内存映射/共享内存区域中正确执行 above

Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/range/adaptor/reversed.hpp>

#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>

namespace bmi = boost::multi_index;
namespace bip = boost::interprocess;

//namespace Shared { struct X; }
//namespace std { template<typename Alloc> struct uses_allocator<Shared::X, Alloc> : std::true_type {}; }

namespace Shared {
    using Segment = bip::managed_mapped_file;
    template <typename T> using Alloc = bip::allocator<T, Segment::segment_manager>;

    using String = boost::interprocess::basic_string<char, std::char_traits<char>, Alloc<char> >;

    struct X {
        using allocator_type = Alloc<char>;

        template <typename Alloc>
        X(int i, std::string const& s, Alloc alloc) : i(i), name(s.begin(), s.end(), alloc) {}

        int i;
        String name;
    };

    typedef boost::multi_index_container<X,
            bmi::indexed_by<
                bmi::ordered_non_unique<
                    bmi::tag<struct by_i>,
                    bmi::composite_key<X,
                        bmi::member<X, int, &X::i>,
                        bmi::member<X, String, &X::name>
                    >
                >
            >,
            Alloc<X>
     > OrderSet;
}

#include <iostream>

int main()
{
    std::cout << std::unitbuf;

    Shared::Segment mmf(bip::open_or_create, "test.data", 10u << 10);

    auto& orderSet_ = *mmf.find_or_construct<Shared::OrderSet>("set")(mmf.get_segment_manager());

    if (orderSet_.empty()) {
        // can't get multi-index to use the scoped allocator "magically" :(
        orderSet_.emplace(1, "one", orderSet_.get_allocator());
        orderSet_.emplace(1, "one", orderSet_.get_allocator());
        orderSet_.emplace(1, "two", orderSet_.get_allocator());
        orderSet_.emplace(2, "three", orderSet_.get_allocator());
        orderSet_.emplace(3, "four", orderSet_.get_allocator());
        orderSet_.emplace(2, "five", orderSet_.get_allocator());
    }

    {
        auto const range = orderSet_/*.get<0>()*/.equal_range(boost::make_tuple(2));

        for (auto& el : boost::make_iterator_range(range))
            std::cout << el.i << "," << el.name << "; ";

        // traverse these in reverse
        std::cout << "\nNow in reverse:\n";
        for (auto& el : range | boost::adaptors::reversed)
            std::cout << el.i << "," << el.name << "; ";
    }

    {
        std::cout << "\nAlso in reverse:\n";
        auto it = orderSet_.get<0>().equal_range(boost::make_tuple(2));
        while(it.second != it.first) {
            auto& el = *(--it.second);
            std::cout << el.i << "," << el.name << "; ";
        }
    }

    std::cout << "\nBye\n";
}

打印

2,five; 2,three; 
Now in reverse:
2,three; 2,five; 
Also in reverse:
2,three; 2,five; 
Bye

两次(证明共享内存段中的所有数据都正确)。

红利

显然,Boost Multi-Index 并不能很好地与作用域分配器配合使用,因为这样会更优雅:

Live On Coliru

#include <set>

#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>

#include <boost/container/set.hpp>
#include <boost/container/scoped_allocator.hpp>

#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/iterator_range.hpp>
namespace bip = boost::interprocess;

namespace Shared {
    using Segment = bip::managed_mapped_file;
    template <typename T> using Alloc = bip::allocator<T, Segment::segment_manager>;

    using String = boost::interprocess::basic_string<char, std::char_traits<char>, Alloc<char> >;

    struct X {
        using allocator_type = Alloc<char>;

        template <typename Alloc>
        X(int i, std::string const& s, Alloc alloc) : i(i), name(s.begin(), s.end(), alloc) {}

        bool operator<(X const& other) const { return i < other.i; }
        bool operator<(int other) const { return i < other; }

        int i;
        String name;
    };

    template <typename T>
        using Multiset = boost::container::multiset<
            T,
            std::less<T>,
            boost::container::scoped_allocator_adaptor<Alloc<X> >
        >;

    using OrderSet = Multiset<X>;
}

#include <iostream>

int main()
{
    std::cout << std::unitbuf;

    Shared::Segment mmf(bip::open_or_create, "test.data", 10u << 10);

    auto& orderSet_ = *mmf.find_or_construct<Shared::OrderSet>("set")(mmf.get_segment_manager());

    if (orderSet_.empty()) {
        // scoped-allocator automatically propagates to the string
        orderSet_.emplace(1, "one");
        orderSet_.emplace(1, "one");
        orderSet_.emplace(1, "two");
        orderSet_.emplace(2, "three");
        orderSet_.emplace(3, "four");
        orderSet_.emplace(2, "five");
    }

    {
        auto const range = orderSet_.equal_range({2, "", orderSet_.get_allocator()});

        for (auto& el : boost::make_iterator_range(range))
            std::cout << el.i << "," << el.name << "; ";

        // traverse these in reverse
        std::cout << "\nNow in reverse:\n";
        for (auto& el : range | boost::adaptors::reversed)
            std::cout << el.i << "," << el.name << "; ";
    }

    {
        std::cout << "\nAlso in reverse:\n";
        auto it = orderSet_.equal_range({2, "", orderSet_.get_allocator()});
        while(it.second != it.first) {
            auto& el = *(--it.second);
            std::cout << el.i << "," << el.name << "; ";
        }
    }

    std::cout << "\nBye\n";
}

【讨论】:

    【解决方案2】:

    您省略了 essential 部分:选择的索引类型。

    不同的索引类型提供不同的接口。

    然后,equal_range 表示有序或无序映射。而且因为在无序地图上颠倒顺序是无意义的,所以我将假设一个有序地图。

    接下来,我用make_tuple 猜测复合键。

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/multi_index/composite_key.hpp>
    
    struct X {
        int i;
        std::string name;
    };
    
    namespace bmi = boost::multi_index;
    typedef boost::multi_index_container<X,
            bmi::indexed_by<
                bmi::ordered_non_unique<
                    bmi::tag<struct by_i>,
                    bmi::composite_key<X,
                        bmi::member<X, int, &X::i>,
                        bmi::member<X, std::string, &X::name>
                    >
                >
            >
     > OrderSet;
    

    那么这里是原生顺序:

    #include <iostream>
    
    int main()
    {
        std::cout << std::unitbuf;
    
        OrderSet orderSet_ {
            { 1, "one" }, { 1, "two" }, { 2, "three" }, { 3, "four" }, { 2, "five" }
        };
    
        auto const range = orderSet_/*.get<0>()*/.equal_range(boost::make_tuple(2));
    
        for (auto& el : boost::make_iterator_range(range))
            std::cout << el.i << "," << el.name << "; ";
    
        // ...
    

    我会使用 Boost Range 来获得相反的顺序:

    #include <boost/range/adaptor/reversed.hpp>
    
       // ...
    
        // traverse these in reverse
        std::cout << "\nNow in reverse:\n";
        for (auto& el : range | boost::adaptors::reversed)
            std::cout << el.i << "," << el.name << "; ";
    }
    

    红利

    当然,您可以手动编写相同的内容:

    std::cout << "\nAlso in reverse:\n";
    auto it = orderSet_.get<0>().equal_range(boost::make_tuple(2));
    while(it.second != it.first) {
        --it.second;
    
        auto& el = *it.second;
        std::cout << el.i << "," << el.name << "; ";
    }
    

    现场演示

    Live On Coliru

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/multi_index/composite_key.hpp>
    #include <boost/range/adaptor/reversed.hpp>
    
    struct X {
        int i; //  = [] { static int gen = 0; return ++gen; }();
        std::string name;
    };
    
    namespace bmi = boost::multi_index;
    typedef boost::multi_index_container<X,
            bmi::indexed_by<
                bmi::ordered_non_unique<
                    bmi::tag<struct by_i>,
                    bmi::composite_key<X,
                        bmi::member<X, int, &X::i>,
                        bmi::member<X, std::string, &X::name>
                    >
                >
            >
     > OrderSet;
    
    #include <iostream>
    
    int main()
    {
        std::cout << std::unitbuf;
    
        OrderSet orderSet_ {
            { 1, "one" }, { 1, "two" }, { 2, "three" }, { 3, "four" }, { 2, "five" }
        };
    
        {
            auto const range = orderSet_/*.get<0>()*/.equal_range(boost::make_tuple(2));
    
            for (auto& el : boost::make_iterator_range(range))
                std::cout << el.i << "," << el.name << "; ";
    
            // traverse these in reverse
            std::cout << "\nNow in reverse:\n";
            for (auto& el : range | boost::adaptors::reversed)
                std::cout << el.i << "," << el.name << "; ";
        }
    
        {
            std::cout << "\nAlso in reverse:\n";
            auto it = orderSet_.get<0>().equal_range(boost::make_tuple(2));
            while(it.second != it.first) {
                auto& el = *(--it.second);
                std::cout << el.i << "," << el.name << "; ";
            }
        }
    
    }
    

    打印

    2,five; 2,three; 
    Now in reverse:
    2,three; 2,five; 
    Also in reverse:
    2,three; 2,five; 
    

    【讨论】:

    • 添加了您没有完全显示为“BONUS TAKE”的循环的固定版本。
    猜你喜欢
    • 2017-08-11
    • 1970-01-01
    • 2013-05-18
    • 2023-03-18
    • 2012-03-15
    • 1970-01-01
    • 2021-10-13
    • 2012-11-29
    • 1970-01-01
    相关资源
    最近更新 更多