views:

53

answers:

2

Is there anyway to loop through an index in a boost::multi_index and perform a replace?

#include <iostream>
#include <string>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>

using namespace boost::multi_index;
using namespace std;

struct name_record
{

  public:

  name_record(string given_name_,string family_name_,string other_name_)
  {
      given_name=given_name_;
      family_name=family_name_;
      other_name=other_name_;
  }

  string given_name;
  string family_name;
  string other_name;

  string get_name() const { return given_name + " " + family_name + " " + other_name; }

  void setnew(string chg)
  {
      given_name = given_name + chg;
      family_name = family_name + chg;
  }

};

struct NameIndex{};

typedef multi_index_container<
  name_record,
  indexed_by<
    ordered_non_unique<
      tag<NameIndex>,
      composite_key
      <
        name_record,
        BOOST_MULTI_INDEX_MEMBER(name_record,string, name_record::given_name),
        BOOST_MULTI_INDEX_MEMBER(name_record,string, name_record::family_name)
      >
    >
  >
> name_record_set;

typedef boost::multi_index::index<name_record_set,NameIndex>::type::iterator IteratorType;
typedef boost::multi_index::index<name_record_set,NameIndex>::type NameIndexType;

void printContainer(name_record_set & ns)
{
  cout << endl << "PrintContainer" << endl << "-------------" << endl;
  IteratorType it1 = ns.begin();
  IteratorType it2 = ns.end  ();

  while (it1 != it2)
  {
       cout<<it1->get_name()<<endl;
       it1++;
  }
  cout << "--------------" << endl << endl;

}


void modifyContainer(name_record_set & ns)
{
  cout << endl << "ModifyContainer" << endl << "-------------" << endl;
  IteratorType it3;
  IteratorType it4;

  NameIndexType & idx1 = ns.get<NameIndex>();
  IteratorType it1 = idx1.begin();
  IteratorType it2 = idx1.end();

  while (it1 != it2)
  {
        cout<<it1->get_name()<<endl;
        name_record nr = *it1;

        nr.setnew("_CHG");
        bool res = idx1.replace(it1,nr);
        cout << "result is: " << res << endl;
        it1++;
  }
  cout << "--------------" << endl << endl;
}

int main()
{
  name_record_set ns;

  ns.insert( name_record("Joe","Smith","ENTRY1")          );
  ns.insert( name_record("Robert","Brown","ENTRY2")       );
  ns.insert( name_record("Robert","Nightingale","ENTRY3") );
  ns.insert( name_record("Marc","Tuxedo","ENTRY4")        );

  printContainer  (ns);
  modifyContainer (ns);
  printContainer  (ns);

  return 0;
}



PrintContainer
-------------
Joe Smith ENTRY1
Marc Tuxedo ENTRY4
Robert Brown ENTRY2
Robert Nightingale ENTRY3
--------------


ModifyContainer
-------------
Joe Smith ENTRY1
result is: 1
Marc Tuxedo ENTRY4
result is: 1
Robert Brown ENTRY2
result is: 1
--------------


PrintContainer
-------------
Joe_CHG Smith_CHG ENTRY1
Marc_CHG Tuxedo_CHG ENTRY4
Robert Nightingale ENTRY3
Robert_CHG Brown_CHG ENTRY2
--------------
A: 
while (it1 != it2)
{
      cout<<it1->get_name()<<endl;
      name_record nr = *it1;

      nr.setnew("_CHG");
      bool res = idx1.replace(it1,nr);
      cout << "result is: " << res << endl;
      it1++;
}

The problem with this code is that, once you've replaced an element x, this is automatically reordered to its new position, so that it1, which keeps pointing to x, "jumps" through the sequence and might skip or revisit elements.

The solution is to add a level of indirection and collect iterators to all the elements before beginning the replacements:

#include <vector>
#include <boost/iterator/counting_iterator.hpp>

void modifyContainer(name_record_set & ns)
{
  cout << endl << "ModifyContainer" << endl << "-------------" << endl;

  NameIndexType & idx1 = ns.get<NameIndex>();
  std::vector<IteratorType> v(
    boost::make_counting_iterator(idx1.begin()),
    boost::make_counting_iterator(idx1.end()));
  std::vector<IteratorType>::iterator it1=v.begin(),
                                      it2=v.end();

  while (it1 != it2)
  {
        cout<<(*it1)->get_name()<<endl;
        name_record nr = **it1;

        nr.setnew("_CHG");
        bool res = idx1.replace(*it1,nr);
        cout << "result is: " << res << endl;
        it1++;
  }
  cout << "--------------" << endl << endl;
}

In case you didn't use it before, the make_counting_iterator bit is just a compact way to populate the vector with iterators to all the elements of ns.

Joaquín M López Muñoz
A: 

Thanks. I had a similar idea but did not know about make_counting_iterator. Also, was afraid of adding overhead since speed is of concern.

Appreciate the response.

Rohit