Why does all elements get replaced when inserting new elements in boost multi_index container?

Viewed 41

When I insert in the main function one after the other elements are inserted properly but when I try to do that through a function all values get replaced by the last elements.

Please refer the following code :

struct X
{
    std::string panelid; // assume unique
    std::string messageid; // assume unique
    std::string tid; // assume non-unique
    std::string name;
};
struct IndexByPanelID {};
struct IndexByMessageID {};
struct IndexByTID {};

typedef  boost::multi_index_container<
        X*, // the data type stored
        boost::multi_index::indexed_by< 
        boost::multi_index::hashed_non_unique<  
        boost::multi_index::tag<IndexByPanelID>, 
        boost::multi_index::member<X, std::string, &X::panelid> 
        >,
        boost::multi_index::hashed_non_unique<  
        boost::multi_index::tag<IndexByMessageID>, 
        boost::multi_index::member<X, std::string, &X::messageid>
        >,
        boost::multi_index::hashed_non_unique<  
        boost::multi_index::tag<IndexByTID>, 
        boost::multi_index::member<X, std::string, &X::tid> 
       >
     >
> Container;
int Insert(X newframe, Container *Cont)
{
    auto& indexByL = c.get<IndexByPanelID>();
    indexByL.insert(&newframe);
    return 0;
}
int main()
{
    Container c;  // empty container
    X x1{ "1", "80", "FE01", "0712"};
    X x2{ "2", "80", "FE02", "0713"};
    X x3{ "3", "180", "FE03", "0714"}; 
    X x4{ "4", "80", "FE04", "0715"};


    Insert(x1,&c); // Doesnt work.
    Insert(x2,&c); // Doesnt work.
    Insert(x3,&c); // Doesnt work.
    Insert(x4,&c); // Doesnt work.

    auto& indexByI3 = c.get<IndexByPanelID>();
    for(auto i = indexByI3.begin(); i != indexByI3.end(); i++)
    {
        X *x = *i;
        std::cout << x->name << '\n';
        std::cout << x->messageid << '\n';
        std::cout << x->panelid << '\n';
        std::cout << x->tid << '\n';
    }

int main()
{
    Container c;  // empty container
    X x1{ "1", "80", "FE01", "0712"};
    X x2{ "2", "80", "FE02", "0713"};
    X x3{ "3", "180", "FE03", "0714"}; 
    X x4{ "4", "80", "FE04", "0715"};
    // Insert some elements
    auto& indexByL = c.get<IndexByPanelID>(); 
    indexByL.insert(&x1); //works fine.
    indexByL.insert(&x2); //works fine.
    indexByL.insert(&x3); //works fine.
    indexByL.insert(&x4); //works fine.

    auto& indexByI3 = c.get<IndexByPanelID>();
    for(auto i = indexByI3.begin(); i != indexByI3.end(); i++)
    {
        X *x = *i;
        std::cout << x->name << '\n';
        std::cout << x->messageid << '\n';
        std::cout << x->panelid << '\n';
        std::cout << x->tid << '\n';
    }
}

Output 1st main :

0715
80
4
FE04
0715
80
4
FE04
0715
80
4
FE04
0715
80
4
FE04

2nd main :

0712
80
1
FE01
0713
80
2
FE02
0714
180
3
FE03
0715
80
4
FE04
1 Answers

You didn't supply working code. You're overusing pointers. The first error makes it so the code can compile:

int Insert(X newframe, Container *Cont)

should be

int Insert(X newframe, Container& c)

The real problem is that you're storing pointers, not frames. The pointer inserted in the Insert function are dangling (they point to the argument frame which is gone after returning from Insert), so the behaviour is Undefined.

Lastly, when inserting new items, there is no need to jump through the extra hoops to select a specific index through which to do so (unless you need an index-specific iterator to the newly inserted element. However, you don't seem to the return value at all).

Here's a simplified version with fixes:

Live On Compiler Explorer

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <iostream>
#include <iomanip>
namespace bmi = boost::multi_index;

#include <string>
struct X { std::string panelid, messageid, tid, name; };

using Container = bmi::multi_index_container<
    X,
    bmi::indexed_by<                                   //
        bmi::hashed_non_unique<                        //
            bmi::tag<struct ByPanelID>,                //
            bmi::member<X, std::string, &X::panelid>   //
            >,                                         //
        bmi::hashed_non_unique<                        //
            bmi::tag<struct ByMessageID>,              //
            bmi::member<X, std::string, &X::messageid> //
            >,                                         //
        bmi::hashed_non_unique<                        //
            bmi::tag<struct ByTID>,                    //
            bmi::member<X, std::string, &X::tid>       //
            >>>;

void Insert(X newframe, Container &c) {
    c.insert(std::move(newframe));
}

int main()
{
    Container c;  // empty container

    Insert({ "1", "80",  "FE01", "0712"},c);
    Insert({ "2", "80",  "FE02", "0713"},c);
    Insert({ "3", "180", "FE03", "0714"},c);
    Insert({ "4", "80",  "FE04", "0715"},c);

    for (auto& [panelid, messageid, tid, name] : c.get<ByPanelID>()) {
        std::cout << std::quoted(name) << ' '      //
                  << std::quoted(messageid) << ' ' //
                  << std::quoted(panelid) << ' '   //
                  << std::quoted(tid) << '\n';
    }
}

Prints

"0712" "80" "1" "FE01"
"0713" "80" "2" "FE02"
"0714" "180" "3" "FE03"
"0715" "80" "4" "FE04"

In fact for this simple demonstration you can do much simpler still: https://godbolt.org/z/hbeeGd7ox

Related