I have a Color class.
I want to create a boost::multi_index_container that uses a color ID and a color position (it's inside a gradient class) as keys.
In order to maintain the code more readable, I've encapsulated the boost implementation in my IndexedColorSet
class.
I want to retrieve the colors ordered by their position. In order to do this I've created a getStartEndPositionIterators
method that returns begin and end iterator of the multi_index_container ordered by position.
My problem is that ending iterator seems to do not work properly. If I create a loop that print position of all iterators from begin to end, they are printed properly, but when I reach the end iterator it cycles indefinitely between last stored value and an invalid value.
How can I retrieve the right end iterator? What I'm doing wrong?
Using boost 1.58.0 and gcc 5.2.1.
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <utility>
#include <iostream>
//////////////////////////////////////////////////////////////
/// Class that must be inserted into multi_index_container. //
//////////////////////////////////////////////////////////////
class Color {
public:
double red;
double green;
double blue;
double alpha;
};
//////////////////////////////////////////////////////
/// Class that implements the multi_index_container //
/// using ordering by id and by position. //
//////////////////////////////////////////////////////
class IndexedColorSet {
public:
// Class that wil be used as multi_index_container template parameter.
class IndexedColor {
public:
int id;
double position;
Color color;
IndexedColor() {}
IndexedColor(int id, double position, const Color &color) :
id(id), position(position), color(color) {}
};
// typedef for multi_index_container iterator
typedef ::boost::multi_index::detail::bidir_node_iterator<boost::multi_index::detail::ordered_index_node<boost::multi_index::detail::index_node_base<IndexedColor, std::allocator<IndexedColor> > > > PositionIterator;
public:
void insertColor(int id, double position, const Color &color);
// Retrieve begin and end iterator using position as index
std::pair<const PositionIterator&, const PositionIterator&> getStartEndPositionIterators() const;
private:
// Tags
struct id{};
struct position{};
// Container creation.
// It creates an ordered_unique index using id
// and a ordered_not_unique index using position
typedef ::boost::multi_index_container<
IndexedColor,
::boost::multi_index::indexed_by<
::boost::multi_index::ordered_unique<
::boost::multi_index::tag<id>,
BOOST_MULTI_INDEX_MEMBER(IndexedColor,int,id)
>,
::boost::multi_index::ordered_non_unique<
::boost::multi_index::tag<position>,
BOOST_MULTI_INDEX_MEMBER(IndexedColor,double,position)
>
>
> PrivateIndexedColorSet;
private:
PrivateIndexedColorSet m_set;
};
// Insert a color associated to id and position.
void IndexedColorSet::insertColor(int id, double position, const Color &color) {
m_set.insert(IndexedColor(id, position, color));
}
// Retrieve a std::pair containing begin and end iterator if multi_index_container
// using the position as key.
std::pair<const IndexedColorSet::PositionIterator&, const IndexedColorSet::PositionIterator&> IndexedColorSet::getStartEndPositionIterators() const {
const auto& positionSet = ::boost::multi_index::get<position>(m_set);
const auto& beginIt = positionSet.begin();
const auto& endIt = positionSet.end();
return std::pair<const PositionIterator&, const PositionIterator&>(beginIt, endIt);
}
int main()
{
IndexedColorSet set;
// Populate the set
int id1 = 1;
int id2 = 2;
int id3 = 3;
double position1 = 0.4;
double position2 = 0.6;
double position3 = 0.3;
Color color1{0.1, 0.3, 0.4, 0.5};
Color color2{0.2, 0.4, 0.5, 0.6};
Color color3{0.3, 0.4, 0.5, 0.6};
set.insertColor(id1, position1, color1);
set.insertColor(id2, position2, color2);
set.insertColor(id3, position3, color3);
// Retrieve ordered position iterators
auto iterators = set.getStartEndPositionIterators();
// Testing that are ordered
// I should obtain
// 0.3
// 0.4
// 0.6
//
// Instead I obtain
// 0.3
// 0.4
// 0.6
// 0
// 0.6
// 0
// 0.6
// 0
// 0.6
// 0... and so on
for (auto it = iterators.first; it != iterators.second; ++it) {
std::cout << it->position << std::endl;
}
return 0;
}
The library author already showed you the main problem. Here's the live-coded cleanup that I have had the joy of going over with you :)
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>
struct Color {
double red;
double green;
double blue;
double alpha;
};
namespace bmi = boost::multi_index;
class IndexedColorSet {
public:
struct IndexedColor {
int id;
double position;
Color color;
};
private:
typedef bmi::multi_index_container<
IndexedColor,
bmi::indexed_by<
bmi::ordered_unique< bmi::tag<struct by_id>, bmi::member<IndexedColor, int, &IndexedColor::id> >,
bmi::ordered_non_unique< bmi::tag<struct by_position>, bmi::member<IndexedColor, double, &IndexedColor::position> >
>
> PrivateIndexedColorSet;
public:
// typedef for multi_index_container iterator
using PositionIterator = PrivateIndexedColorSet::index<by_position>::type::const_iterator;
using PositionRange = boost::iterator_range<PositionIterator>;
void insertColor(int id, double position, const Color &color);
PositionRange getPositionRange() const;
private:
PrivateIndexedColorSet m_set;
};
// Insert a value
void IndexedColorSet::insertColor(int id, double position, const Color &color) {
m_set.insert({id, position, color});
}
// Retrieve the full range by_position
IndexedColorSet::PositionRange IndexedColorSet::getPositionRange() const {
auto& positionSet = bmi::get<by_position>(m_set);
auto beginIt = positionSet.begin();
auto endIt = positionSet.end();
return { beginIt, endIt };
}
int main() {
IndexedColorSet set;
// Populate the set
set.insertColor(1, 0.4, {0.1, 0.3, 0.4, 0.5});
set.insertColor(2, 0.6, {0.2, 0.4, 0.5, 0.6});
set.insertColor(3, 0.3, {0.3, 0.4, 0.5, 0.6});
// Retrieve ordered position iterators
auto iterators = set.getPositionRange();
for (auto& ic : iterators) {
std::cout << ic.position << std::endl;
}
}
Prints
0.3
0.4
0.6