Nespl NS3
Nespl NS3

Reputation: 52

MultiIndex Container equal_range .. get output in reverse order

I have a boost::multiIndex container. say :

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;          
 }
}

the program crash with offset_ptr error. for the program it has value on increasing order. so I want highest value first then so-on.. Is any a over-loaded function of boost::equal_range(..., [&](const auto a, const auto b)-> decltype bool{return a > b;}); Something like this. it's like reverse_iterator of equal_range()

UPDATE: multi-Index container is accessed by one producer and many consumer. while consumer is finding equal_range, producer add or delete elements and consumer throw the error... this is only happens when i am reverse iterating...

Upvotes: 1

Views: 731

Answers (2)

sehe
sehe

Reputation: 393064

Even further out on a limb, since you said offset_ptr you might be using Boost Interprocess allocators.

Here's a demo that shows how you'd do the above correctly in a memory mapped/shared memory area:

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";
}

Prints

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

Twice (proving all data was correctly in the shared memory segment).

BONUS TAKE

Apparently, Boost Multi-Index doesn't quite play completely well with the scoped allocator, because it would be more elegant with that:

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";
}

Upvotes: 1

sehe
sehe

Reputation: 393064

You're leaving out the essential part: the chosen index type.

Different index types afford different interfaces.

Then again, equal_range implies a ordered or unordered map. And because reversing the order on an unordered map is non-sense, I'm going to assume an ordered map.

Next, with make_tuple there I'm guessing composite keys.

#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;

Then here's the native order:

#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 << "; ";

    // ...

And I'd use Boost Range to get the reverse order:

#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 << "; ";
}

BONUS TAKE

Of course you can write the same manually:

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 Demo

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 << "; ";
        }
    }

}

Prints

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

Upvotes: 6

Related Questions