boost multi index access to hashed unique index - c++

This code is adopted from the boost multi-index "mru" example:
http://www.boost.org/doc/libs/1_46_1/libs/multi_index/example/serialization.cpp
I have code that is doing something similiar as a boost::unordered_map, but I would really like to add the mru functionality from this example.
I would like to make this code work as close to having a boost::unordered_map as possible. The key feature for me is the [] operator of the unordered_map.
The final lines of main() are broken and above each line as a comment is my question.
Thanks in advance to all answers comments.
#include <algorithm>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <iostream>
using namespace boost::multi_index;
class my_struct {
public:
my_struct(int in_a, std::string in_b) : a(in_a), b(in_b) {}
int a;
std::string b;
bool operator==(const my_struct &rhs) const
{
return (a == rhs.a);
}
bool operator!=(const my_struct &rhs) const
{
return !(*this == rhs);
}
friend std::ostream& operator<<(std::ostream &out, const my_struct&ms);
};
std::ostream& operator<<(std::ostream &out, my_struct &ms)
{
out << ms.a << " " << ms.b << std::endl;
return out;
}
inline std::size_t
hash_value(const my_struct &val)
{
return boost::hash_value(val.a);
}
// tags for multi_index
struct umap {};
template <typename Item>
class mru_list
{
typedef multi_index_container<
Item,
indexed_by<
sequenced<>,
hashed_unique<boost::multi_index::tag<umap>, identity<Item> >
>
> item_list;
public:
typedef Item item_type;
typedef typename item_list::iterator iterator;
mru_list(std::size_t max_num_items_):max_num_items(max_num_items_){}
void insert(const item_type& item)
{
std::pair<iterator,bool> p=il.push_front(item);
if(!p.second){ /* duplicate item */
il.relocate(il.begin(),p.first); /* put in front */
}
else if(il.size()>max_num_items){ /* keep the length <= max_num_items */
il.pop_back();
}
}
iterator begin(){return il.begin();}
iterator end(){return il.end();}
//private:
item_list il;
std::size_t max_num_items;
};
int main()
{
mru_list<my_struct> mru(10);
my_struct one(1, "One");
mru.insert(one);
mru.insert(my_struct(2, "Two"));
mru.insert(my_struct(3, "Three"));
mru.insert(one);
std::cout<<"most recently entered terms:"<<std::endl;
for (mru_list<my_struct>::iterator itr = mru.begin(); itr != mru.end(); ++itr) {
std::cout << itr->a << std::endl;
}
// what is my return type?
mru.il.get<umap>();
// Why doesn't this work?
mru_list<my_struct>::iterator itr = mru.il.get<umap>().find(one);
// Why doesn't this have a [] operator like boost:unordered_map
mru.il.get<umap>()[1] = "foobar";
return 0;
}

// what is my return type?
mru.il.get<umap>();
Its return type is the type of your umap index, which is:
typedef typename boost::multi_index::index<
item_list
, umap
>::type hashed_index_t;
mru_list<my_struct>::hashed_index_t& hashed_index = mru.il.get<umap>();
In C++11 it is easier with auto:
auto& hashed_index = mru.il.get<umap>();
// Why doesn't this work?
mru_list<my_struct>::iterator itr = mru.il.get<umap>().find(one);
find() returns an iterator of umap (second) index and the above statement is assigning it to the iterator of the first index. There are projection operations to convert from one index iterator type to another iterator type of the same multi-index container, e.g.:
mru_list<my_struct>::iterator itr = project<0>(mru.il, hashed_index.find(one));
// Why doesn't this have a [] operator like boost:unordered_map
Can't say why, it just doesn't.

Related

How to extend the functionality of vector class as well as its iterator C++

I would like to find a clean way to slightly change the way std::vector operates.
Problem background
I need to be able to have index in the vector where the pointer would essentially slip one back, so for example; if my vector contains {0,1,2,3,4,5} and index [3] needs to slip, when iterating through the vector it should return: 0, 1, 2, 3, 3, 4, 5
Problem
Without rewriting the entire vector class and implementing my changes, is it possible to inherit std::vector<T> into a custom class, and override the behavior of the iterators ++ operator as well as the vector::begin()? (to add memory of the fact that it has already slipped one and not to slip again)
What I have tried
I have tried a basically implementation so far, but have gotten stuck when trying to override the begin() function.
#include <vector>
#include <iostream>
#include <iterator>
template <typename T>
class myVector : public std::vector<T>{
public:
class myIterator : public std::vector<T>::iterator
{
// call vector's iterotor constructor
//override
// iterator& operator++(void){}
// iterator operator++(int){}
};
using std::vector<T>::vector; // use the constructor from vector
// extend the begin function, but include original operation
myVector::myIterator begin() const
{
std::cout << "hi\n"; // testing to see if begin has been overriden properly
return std::vector<T>::begin();
}
};
// print out the vector
template <typename T>
std::ostream& operator<<(std::ostream& os, const myVector<T>& v)
{
auto begin=v.begin();
while (begin!=v.end())
{
os << *begin;
++begin;
if(begin!=v.end())
{
os << ',';
}
}
return os;
}
int main() {
myVector<int> vec = {1,2,3,4};
std::cout << vec << '\n';
}
If you have an alternative clean solutions, it is also welcome.
Thanks
I would never inherit from std::vector publicly. It is just too easy to be used wrong and you have no control over misuse. std::vector has no virtual destructor!
Iterators seems to be the appropriate customization point. Much more is needed to get a fully compliant iterator, but this is enough for a working example:
#include <iterator>
#include <vector>
#include <iostream>
template <typename It>
struct slippery_iterator {
bool slipped = false;
It to_be_repeated;
It iterator;
slippery_iterator(It iterator,It to_be_repeated) : iterator(iterator),to_be_repeated(to_be_repeated){}
slippery_iterator& operator++(){
if (!slipped && iterator == to_be_repeated){
slipped = true;
return *this;
}
++iterator;
return *this;
}
typename std::iterator_traits<It>::reference operator*(){
return *iterator;
}
bool operator!=(It other) { return iterator != other;}
};
template <typename It>
slippery_iterator<It> make_slippery_iterator(It it,It slip){
return {it,slip};
}
int main() {
std::vector<int> x{1,2,3,4,5};
auto begin = make_slippery_iterator(x.begin(),x.begin()+2);
for (; begin != x.end(); ++begin){
std::cout << *begin;
}
}
Output:
123345
PS: Note that I am used to C++11 where you need a make_x helper. It isnt needed with more recent standards.
As others already said, just define a custom iterator. If you moreover want the nice range iteration, you can then just define a custom range class returning your custom iterators.
See a working example below (needs --std=c++17):
#include <vector>
#include <iostream>
template<typename T>
class SlipIterator {
private:
using iterator = typename std::vector<T>::const_iterator;
iterator i;
iterator slip;
bool has_slipped;
public:
SlipIterator(iterator i, iterator slip): i(i), slip(slip), has_slipped(false) {}
SlipIterator &operator++() {
if ((!has_slipped) && (i == slip))
has_slipped = true;
else
++i;
return *this;
}
bool operator!=(SlipIterator<T> &the_end) const { return i != the_end.i; }
const T &operator*() const { return *i; }
};
template<typename T>
class SlipperyRange {
private:
const std::vector<T> &v;
size_t slip_index;
public:
SlipperyRange(const std::vector<T> &v, size_t slip_index) : v(v), slip_index(slip_index) {}
SlipIterator<T> begin() const { return SlipIterator<T>(v.cbegin(), v.cbegin() + slip_index); }
SlipIterator<T> end() const { return SlipIterator<T>(v.cend(), v.cend()); }
};
int main() {
std::vector<int> v{1,2,3,4,5};
for(const int i: SlipperyRange{v, 2})
std::cout << i << ' ';
std::cout << '\n';
return 0;
}

Create iterator for class using map

I am implementing a class using a map, like so:
class Letras {
typedef std::pair<int, int> p_c;
std::map<char, p_c> bolsa;
public:
...
};
And I'd like to implement the functionality of an iterator, to be used like so:
Letras l;
Letras::iterator it;
for ( it = l.begin(); it != l.end(); ++it )
cout << *it << endl;
So that it prints out the chars from the std::map first component (key).
Thanks in advance.
More information about Letras
Letras is a class that implements a Scrubble-like letter set, the map is used to represent a set of letras (letters in Spanish) like the following:
{{letter_1, {quantity_1, score_1}}, {letter_2, {quantity_2, score_2}}, ..., {letter_n, {quantity_n, score_n}}}
Where letter_i is a certain letter from the set, quantity_i is the amount of times that letter is in the set (a Scrubble-like letter set can have various identical letters) and score_i is the letter score.
I have used a map because a letter can only have an associated score, so that in this class, i != j => letter_i != letter_j.
The utility of Letras is to find the best words formed by a certain letter set from a Diccionario class (dictionary), another class that has implemented a DAWG structure for fast search and lookup.
You can do something like this:
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
class Letras {
private:
typedef std::pair<int, int> p_c;
std::map<char, p_c> bolsa;
public:
class iterator {
private:
std::map<char, p_c>::iterator it;
public:
iterator(const std::map<char, p_c>::iterator &it) {
this->it = it;
}
// EDIT: Removing Copy Semantics
iterator(const iterator &it) = delete;
iterator &operator =(const iterator &it) = delete;
// EDIT: Enabling Move Semantics
iterator(iterator &&it) = default;
iterator &operator =(iterator &&it) = default;
auto &operator *() const {
return it->first;
}
iterator &operator ++(int) {
this->it++;
return *this;
}
bool operator !=(const iterator &it) {
return this->it != it.it;
}
};
auto begin() {
return iterator(bolsa.begin());
}
auto end() {
return iterator(bolsa.end());
}
void insert(const std::pair<char, p_c> &p) {
bolsa.insert(p);
}
};
int main() {
Letras b;
b.insert(make_pair('f', make_pair(1, 2)));
b.insert(make_pair('g', make_pair(1, 2)));
b.insert(make_pair('h', make_pair(1, 2)));
for (auto it = b.begin(); it != b.end(); it++) {
cout << *it << endl;
}
return 0;
}

The base() function of reverse_iterator

From this old question
C++ custom collection reverse_iterator with similar behaviour to std::vector implementation
I thought that the lines (after revising his design a bit)
template <typename Iterator>
class reverse_iterator {
Iterator _it;
public:
reverse_iterator(const Iterator& it):_it(it) { }
Iterator base() const {Iterator it = _it; return --it;}
typename Iterator::value_type& operator*() const {return *base();}
// ...
};
were correct (no one replied that they were incorrect). But it doesn't give the same output that this test with std::reverse_iterator gives:
#include <iostream>
#include <iterator>
#include <vector>
int main () {
std::vector<int> myvector = {1,2,3};
std::vector<int>::iterator it = std::next(myvector.begin());
std::reverse_iterator<std::vector<int>::iterator> r(it);
std::cout << *it << '\n'; // 2
std::cout << *r << '\n'; // 1
std::cout << *r.base() << '\n'; // 2
}
which seems to show that the lines should instead be
template <typename Iterator>
class reverse_iterator {
Iterator _it;
public:
reverse_iterator(const Iterator& it):_it(it) { }
Iterator base() const { return _it; }
typename Iterator::value_type& operator*() const { return *--base(); }
// ...
};
which would indeed give the same output as with std::reverse_iterator. What is correct here?

I want use set to remove duplicate element and keep the order when they insert

I want use set to remove duplicate elements and keep their order by the same time. So I try to change the compare parameter to let they sort with the order they are inserted.
#include <set>
#include <iostream>
using namespace std;
template <class T>
struct compare
{
bool operator() (T x, T y)
{
return true;
}
};
void main()
{
set<int,compare<int>> one;
one.insert(5);
one.insert(3);
one.insert(9);
one.insert(1);
one.insert(5);
one.insert(5);
}
The expression from the IDE is :invaild operator <
std::set relies on the comparator to maintain strict weak ordering and ensure each value is unique. You can't have a std::set sort in the order they are inserted.
One possible solution is to have two containers, a std::set to contain the unique elements and a std::vector index to keep the order they were inserted. The vector could perhaps contain iterators into the set.
It might be convenient to encapsulate the two containers in your own class with its own iterator. Here is a bare-bones implementation:
class MySetIterator {
std::vector<std::set<int>::iterator>::iterator pos;
public:
MySetIterator(std::vector<std::set<int>::iterator>::iterator pos) : pos(pos) {}
int operator*() { return **pos; }
MySetIterator& operator++() { ++pos; return *this; }
bool operator!=(const MySetIterator& rhs) { return pos != rhs.pos; }
};
class MySet {
std::set<int> vals;
std::vector<std::set<int>::iterator> order;
public:
void insert(int val) {
auto ret = vals.insert(val);
if (ret.second)
order.push_back(ret.first);
}
MySetIterator begin() { return {order.begin()}; }
MySetIterator end() { return {order.end()}; }
};
int main() {
MySet my_set;
my_set.insert(5);
my_set.insert(3);
my_set.insert(9);
my_set.insert(1);
my_set.insert(5);
my_set.insert(5);
for (int val : my_set)
std::cout << val << " ";
}
Other possible solution is to use boost::multi_index.
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
namespace boost_mi = boost::multi_index;
typedef boost::multi_index_container<
int,
boost_mi::indexed_by<
boost_mi::random_access<>, // preserves insertion order
boost_mi::ordered_unique<boost_mi::identity<int> > // removes duplicates
>
> MySet;
int main()
{
MySet my_set;
my_set.push_back(5);
my_set.push_back(3);
my_set.push_back(9);
my_set.push_back(1);
my_set.push_back(5);
my_set.push_back(5);
for (auto val : my_set) {
std::cout << val << std::endl;
}
return 0;
}

Use of for_each on map elements

I have a map where I'd like to perform a call on every data type object member function. I yet know how to do this on any sequence but, is it possible to do it on an associative container?
The closest answer I could find was this: Boost.Bind to access std::map elements in std::for_each. But I cannot use boost in my project so, is there an STL alternative that I'm missing to boost::bind?
If not possible, I thought on creating a temporary sequence for pointers to the data objects and then, call for_each on it, something like this:
class MyClass
{
public:
void Method() const;
}
std::map<int, MyClass> Map;
//...
std::vector<MyClass*> Vector;
std::transform(Map.begin(), Map.end(), std::back_inserter(Vector), std::mem_fun_ref(&std::map<int, MyClass>::value_type::second));
std::for_each(Vector.begin(), Vector.end(), std::mem_fun(&MyClass::Method));
It looks too obfuscated and I don't really like it. Any suggestions?
C++11 allows you to do:
for (const auto& kv : myMap) {
std::cout << kv.first << " has value " << kv.second << std::endl;
}
C++17 allows you to do:
for (const auto& [key, value] : myMap) {
std::cout << key << " has value " << value << std::endl;
}
using structured binding.
UPDATE:
const auto is safer if you don't want to modify the map.
You can iterate through a std::map object. Each iterator will point to a std::pair<const T,S> where T and S are the same types you specified on your map.
Here this would be:
for (std::map<int, MyClass>::iterator it = Map.begin(); it != Map.end(); ++it)
{
it->second.Method();
}
If you still want to use std::for_each, pass a function that takes a std::pair<const int, MyClass>& as an argument instead.
Example:
void CallMyMethod(std::pair<const int, MyClass>& pair) // could be a class static method as well
{
pair.second.Method();
}
And pass it to std::for_each:
std::for_each(Map.begin(), Map.end(), CallMyMethod);
C++14 brings generic lambdas.
Meaning we can use std::for_each very easily:
std::map<int, int> myMap{{1, 2}, {3, 4}, {5, 6}, {7, 8}};
std::for_each(myMap.begin(), myMap.end(), [](const auto &myMapPair) {
std::cout << "first " << myMapPair.first << " second "
<< myMapPair.second << std::endl;
});
I think std::for_each is sometimes better suited than a simple range based for loop. For example when you only want to loop through a subset of a map.
How about a plain C++? (example fixed according to the note by #Noah Roberts)
for(std::map<int, MyClass>::iterator itr = Map.begin(), itr_end = Map.end(); itr != itr_end; ++itr) {
itr->second.Method();
}
It's unfortunate that you don't have Boost however if your STL implementation has the extensions then you can compose mem_fun_ref and select2nd to create a single functor suitable for use with for_each. The code would look something like this:
#include <algorithm>
#include <map>
#include <ext/functional> // GNU-specific extension for functor classes missing from standard STL
using namespace __gnu_cxx; // for compose1 and select2nd
class MyClass
{
public:
void Method() const;
};
std::map<int, MyClass> Map;
int main(void)
{
std::for_each(Map.begin(), Map.end(), compose1(std::mem_fun_ref(&MyClass::Method), select2nd<std::map<int, MyClass>::value_type>()));
}
Note that if you don't have access to compose1 (or the unary_compose template) and select2nd, they are fairly easy to write.
For fellow programmers who stumble upon this question from google, there is a good way using boost.
Explained here : Is it possible to use boost::foreach with std::map?
Real example for your convenience :
// typedef in include, given here for info :
typedef std::map<std::string, std::string> Wt::WEnvironment::CookieMap
Wt::WEnvironment::CookieMap cookie_map = environment.cookies();
BOOST_FOREACH( const Wt::WEnvironment::CookieMap::value_type &cookie, cookie_map )
{
std::cout << "cookie : " << cookie.first << " = " << cookie.second << endl;
}
enjoy.
Will it work for you ?
class MyClass;
typedef std::pair<int,MyClass> MyPair;
class MyClass
{
private:
void foo() const{};
public:
static void Method(MyPair const& p)
{
//......
p.second.foo();
};
};
// ...
std::map<int, MyClass> Map;
//.....
std::for_each(Map.begin(), Map.end(), (&MyClass::Method));
Just an example:
template <class key, class value>
class insertIntoVec
{
public:
insertIntoVec(std::vector<value>& vec_in):m_vec(vec_in)
{}
void operator () (const std::pair<key, value>& rhs)
{
m_vec.push_back(rhs.second);
}
private:
std::vector<value>& m_vec;
};
int main()
{
std::map<int, std::string> aMap;
aMap[1] = "test1";
aMap[2] = "test2";
aMap[3] = "test3";
aMap[4] = "test4";
std::vector<std::string> aVec;
aVec.reserve(aMap.size());
std::for_each(aMap.begin(), aMap.end(),
insertIntoVec<int, std::string>(aVec)
);
}
From what I remembered, C++ map can return you an iterator of keys using map.begin(), you can use that iterator to loop over all the keys until it reach map.end(), and get the corresponding value:
C++ map
I wrote this awhile back to do just what you're looking for.
namespace STLHelpers
{
//
// iterator helper type for iterating through the *values* of key/value collections
//
/////////////////////////////////////////////
template<typename _traits>
struct _value_iterator
{
explicit _value_iterator(typename _traits::iterator_type _it)
: it(_it)
{
}
_value_iterator(const _value_iterator &_other)
: it(_other.it)
{
}
friend bool operator==(const _value_iterator &lhs, const _value_iterator &rhs)
{
return lhs.it == rhs.it;
}
friend bool operator!=(const _value_iterator &lhs, const _value_iterator &rhs)
{
return !(lhs == rhs);
}
_value_iterator &operator++()
{
++it;
return *this;
}
_value_iterator operator++(int)
{
_value_iterator t(*this);
++*this;
return t;
}
typename _traits::value_type &operator->()
{
return **this;
}
typename _traits::value_type &operator*()
{
return it->second;
}
typename _traits::iterator_type it;
};
template<typename _tyMap>
struct _map_iterator_traits
{
typedef typename _tyMap::iterator iterator_type;
typedef typename _tyMap::mapped_type value_type;
};
template<typename _tyMap>
struct _const_map_iterator_traits
{
typedef typename _tyMap::const_iterator iterator_type;
typedef const typename _tyMap::mapped_type value_type;
};
}
Here is an example of how you can use for_each for a map.
std::map<int, int> map;
map.insert(std::pair<int, int>(1, 2));
map.insert(std::pair<int, int>(2, 4));
map.insert(std::pair<int, int>(3, 6));
auto f = [](std::pair<int,int> it) {std::cout << it.first + it.second << std::endl; };
std::for_each(map.begin(), map.end(), f);