boost multi index insertion errors with boost::tuple of references as the key for a ordered_unique index

StackOverflow https://stackoverflow.com/questions/12320912

Question

I have boiled this down to the simplest example code I could think of.

I have a boost multi indexed by a member:

typedef const boost::tuple<const uint32_t &, const uint8_t &> key_type;

Doing this seems make the multi-index think every item is equal (size is never > 1)

I am storing a structure with 2 members, I would like a unique key for the multi-index to be both of those members. I thought making a tuple of references would accomplish this fairly simply. It does not behave as I expected though. It seems when the items in the tuple are references every new item conflicts with the existing item. It is probably also worth noting that simply moving away from references will make the code behave as I expect, but that doesn't help me understand why the reference case isn't working.

#include <stdint.h>
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/tag.hpp>
#include "boost/tuple/tuple.hpp"
#include "boost/tuple/tuple_comparison.hpp"

namespace bmi = ::boost::multi_index;

class MyMultiIndex {
public:
    MyMultiIndex() {}
    ~MyMultiIndex() {}

    // Switching away from references fixes everything....
    typedef const boost::tuple<const uint32_t &, const uint8_t &> key_type;
    //typedef const boost::tuple<const uint32_t, const uint8_t> key_type;

    struct Item {
        const uint8_t thing1;
        const uint32_t thing2;
        key_type key;

        Item(const uint8_t &a1, const uint32_t &a2)
                : thing1(a1), thing2(a2), key(thing2, thing1)
        {}
    };

    struct key_idx {};

    typedef bmi::multi_index_container<
        Item,
        bmi::indexed_by<
            bmi::ordered_unique<bmi::tag<key_idx>,
                                bmi::member<Item, key_type, &Item::key>
            >
        >
    > imsi_map_type;

    typedef imsi_map_type::index<key_idx>::type key_idx_type;

    void insert(const uint8_t &a1, const uint32_t &a2)
    {
        Item item(a1, a2);

        key_idx_type &idx(mi.get<key_idx>());
        std::pair<key_idx_type::iterator, bool> ret = idx.insert(item);

        if (!ret.second) {
            std::cout << "itr = " << (int)ret.first->thing1 << " " << ret.first->thing2 << std::endl;
        }
    }
private:
    imsi_map_type mi;
};

int
main()
{
    MyMultiIndex mindex;

    mindex.insert(1, 10);
    mindex.insert(1, 20);
    mindex.insert(3, 10);

    return 0;
}

As stated in the example if I make the tuple hold values and not references everyting works as I am expecting.

I have spent time looking into various possibilities (dangling references, comparing boost:tuples of references in a smaller program with no multi-index, etc)

Here is my compilation command: g++ -O0 -ggdb -Wall -Werror test.cc -lboost_system -lpthread

running the program gives:

itr = 1 10 itr = 1 10

Showing that even though I am trying to insert 1,20 and 3,10 the multi seems to think they are equal to 1,10.

I am quite perplexed. Any and all help is appreciated.

Was it helpful?

Solution

The copy semantics of Item, as implemented by its default copy ctor, are flawed. Provide a copy ctor like this:

Item(const Item& x)
  : thing1(x.thing1), thing2(x.thing2), key(thing2, thing1)
{}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top