Wrapping unordered_map to build an unmodifiable (immutable) map - c++

I am playing with container types, gettingmore into the details and try to build an unmodifiable (or immutable) map.
For that, I built some kind like copy-constructor to add all elements from another unordered_map into my UnmodifiableMap (which is basically a wrapper for std::unordered_map). To make it unmodifiable, I only provide const iterators and read-only methods. But I get stuck with the constructor, I am sure I miss something, maybe somebody can point me the problem here. Maybe it is a totally wrong way like this, but his is what tried so far:
template <typename Key, typename T,
typename HashFcn = std::hash<Key>,
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator<std::pair<const Key, T> > >
class UnmodifiableMap {
public:
// The actual data
typedef std::unordered_map<T, Key, HashFcn, EqualKey> base;
typedef Key key_type;
typedef T data_type;
typedef T mapped_type;
typedef std::pair<const key_type, data_type> value_type;
typedef HashFcn hasher;
typedef EqualKey key_equal;
typedef Alloc allocator_type;
typedef typename base::size_type size_type;
typedef typename base::const_iterator const_iterator;
typedef typename base::iterator iterator;
private:
base _map;
/**
* Constructs an empty unordered_map
*/
UnmodifiableMap(
size_type n = 0, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type &alloc = allocator_type())
: _map(n, hf, eql, alloc) {}
public:
/** Constructs a copy of unordered_map */
UnmodifiableMap(const base& other)
: _map(static_cast<const base&>(other)) {}
~UnmodifiableMap() {}
iterator begin() { return _map.begin(); }
iterator end() { return _map.end(); }
const_iterator begin() const { return _map.begin(); }
const_iterator end() const { return _map.end(); }
bool empty() const { return _map.empty(); }
bool contains(const key_type& key) const {
return _map.find(key) != _map.end(); }
};
And here the main body:
int main(int argc, char **argv) {
typedef std::unordered_map<int, std::string> Items;
Items map;
map[1] = "first string";
map[4] = "string 4";
map[5] = "string 5";
map[22] = "string 22";
map[12] = "string 12";
map[18] = "string 18";
typedef UnmodifiableMap<int, std::string> ReadOnlyItems;
ReadOnlyItems readonlymap(map);
return 0;
}
The error I get is
Unmodifiable_map.cpp: In function ‘int main(int, char**)’:
Unmodifiable_map.cpp:56:25: error: no matching function for call to ‘UnmodifiableMap<int, std::basic_string<char> >::UnmodifiableMap(Items&)’
Unmodifiable_map.cpp:56:25: note: candidates are:
Unmodifiable_map.h:45:2: note: UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::UnmodifiableMap(const base&) [with Key = int, T = std::basic_string<char>, HashFcn = std::hash<int>, EqualKey = std::equal_to<int>, Alloc = std::allocator<std::pair<const int, std::basic_string<char> > >, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::base = std::unordered_map<std::basic_string<char>, int, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<const std::basic_string<char>, int> > >]
Unmodifiable_map.h:45:2: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘const base& {aka const std::unordered_map<std::basic_string<char>, int, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<const std::basic_string<char>, int> > >&}’
Unmodifiable_map.h:37:2: note: UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::UnmodifiableMap(UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with Key = int, T = std::basic_string<char>, HashFcn = std::hash<int>, EqualKey = std::equal_to<int>, Alloc = std::allocator<std::pair<const int, std::basic_string<char> > >, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::size_type = long unsigned int, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::hasher = std::hash<int>, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::key_equal = std::equal_to<int>, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::allocator_type = std::allocator<std::pair<const int, std::basic_string<char> > >]
Unmodifiable_map.h:37:2: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘long unsigned int’
Unmodifiable_map.h:14:7: note: UnmodifiableMap<int, std::basic_string<char> >::UnmodifiableMap(const UnmodifiableMap<int, std::basic_string<char> >&)
Unmodifiable_map.h:14:7: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘const UnmodifiableMap<int, std::basic_string<char> >&’
Unmodifiable_map.h:14:7: note: UnmodifiableMap<int, std::basic_string<char> >::UnmodifiableMap(UnmodifiableMap<int, std::basic_string<char> >&&)
Unmodifiable_map.h:14:7: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘UnmodifiableMap<int, std::basic_string<char> >&&’
Hope somebody can shed some light on that. Also I think I need to do more in the copy constructor, propably copy the elements (like a swap function) and an implementaton of operator= ?!

You are swapping the role of T and Key in your base type definition inside your wrapper class:
template <typename Key, typename T,
typename HashFcn = std::hash<Key>,
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator<std::pair<const Key, T> > >
class UnmodifiableMap {
public:
// typedef std::unordered_map<T, Key, HashFcn, EqualKey> base; // ERROR
typedef std::unordered_map<Key, T, HashFcn, EqualKey> base; // OK
...
Hence, your underlying map ends up being an unordered_map<string, int> rather than an unordered_map<int, string>, and the compiler complains about the mismatch.
Also notice, that you have an unnecessary static_cast in your constructor:
UnmodifiableMap(const base& other)
// : _map(static_cast<const base&>(other)) {} // NOT NEEDED. Just do:
: _map(other) {}

Related

std::set_difference on map and a vector throws conversion errors

The following code should compute the difference of a map and a vector
std::map<int, int> cursorMap;
QVector<User> userList;
...
std::vector<int> offlineUserIds{};
std::vector<int>::iterator it;
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(),
[](const std::pair<int, int> &e, const User &u){ return u.getId() == e.first; });
before invoking set_difference, userList is converted to a std::vector and sorted. The problem is that it gaves me the following errors:
error: cannot convert 'std::pair<const int, int>' to 'int' in assignment
...
error: no match for call to '(TextEditor::updateCursorMap(QVector<User>)::<lambda(const std::pair<int, int>&, const User&)>) (User&, std::pair<const int, int>&)'
{ return bool(_M_comp(*__it1, *__it2)); }
...
note: candidate: 'TextEditor::updateCursorMap(QVector<User>)::<lambda(const std::pair<int, int>&, const User&)>'
...
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](const std::pair<int, int> &e, const User &u){ return u.getId() == e.first; });
...
note: no known conversion for argument 1 from 'User' to 'const std::pair<int, int>&'
EDIT:
I tried the following code
std::map<int, int> cursorMap;
QVector<User> userList;
...
std::vector<std::pair<int, int>> offlineUserIds{};
std::vector<std::pair<int, int>>::iterator it;
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](const std::pair<int, int> &e, const User &u){ return e.first < u.getId(); });
but now it gives me
note: candidate: 'TextEditor::updateCursorMap(QVector<User>)::<lambda(const std::pair<int, int>&, const User&)>'
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](const std::pair<int, int> &e, const User &u){ return e.first < u.getId(); });
...
note: no known conversion for argument 1 from 'User' to 'const std::pair<int, int>&'
EDIT2:
Here is a minimal reproducible example:
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
class User {
int id;
public:
int getId() const { return id; }
};
int main() {
std::vector<User> newUserList{};
std::map<int, int> cursorMap{};
std::vector<User> userList = std::vector(newUserList.begin(), newUserList.end());
std::sort(userList.begin(), userList.end(), [](const User &u1, const User &u2) { return u1.getId() < u2.getId(); });
std::vector<std::pair<int, int>> offlineUserIds{};
std::vector<std::pair<int, int>>::iterator it;
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](std::pair<int, int> e, User u){ return e.first < u.getId(); });
return 0;
}
build output
In file included from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/stl_algobase.h:71,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/char_traits.h:39,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/ios:40,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/ostream:38,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/iostream:39,
from D:\asant\workspace\CLionProjects\untitled\main.cpp:1:
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h: In instantiation of 'constexpr bool __gnu_cxx::__ops::_Iter_comp_iter<_Compare>::operator()(_Iterator1, _Iterator2) [with _Iterator1 = __gnu_cxx::__normal_iterator<User*, std::vector<User> >; _Iterator2 = std::_Rb_tree_iterator<std::pair<const int, int> >; _Compare = main()::<lambda(std::pair<int, int>, User)>]':
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/stl_algo.h:5343:17: required from '_OutputIterator std::__set_difference(_InputIterator1, _InputIterator1, _InputIterator2, _InputIterator2, _OutputIterator, _Compare) [with _InputIterator1 = std::_Rb_tree_iterator<std::pair<const int, int> >; _InputIterator2 = __gnu_cxx::__normal_iterator<User*, std::vector<User> >; _OutputIterator = __gnu_cxx::__normal_iterator<std::pair<int, int>*, std::vector<std::pair<int, int> > >; _Compare = __gnu_cxx::__ops::_Iter_comp_iter<main()::<lambda(std::pair<int, int>, User)> >]'
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/stl_algo.h:5447:46: required from '_OIter std::set_difference(_IIter1, _IIter1, _IIter2, _IIter2, _OIter, _Compare) [with _IIter1 = std::_Rb_tree_iterator<std::pair<const int, int> >; _IIter2 = __gnu_cxx::__normal_iterator<User*, std::vector<User> >; _OIter = __gnu_cxx::__normal_iterator<std::pair<int, int>*, std::vector<std::pair<int, int> > >; _Compare = main()::<lambda(std::pair<int, int>, User)>]'
D:\asant\workspace\CLionProjects\untitled\main.cpp:21:188: required from here
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h:143:18: error: no match for call to '(main()::<lambda(std::pair<int, int>, User)>) (User&, std::pair<const int, int>&)'
{ return bool(_M_comp(*__it1, *__it2)); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h:143:18: note: candidate: 'bool (*)(std::pair<int, int>, User)' <conversion>
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h:143:18: note: candidate expects 3 arguments, 3 provided
D:\asant\workspace\CLionProjects\untitled\main.cpp:21:156: note: candidate: 'main()::<lambda(std::pair<int, int>, User)>'
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](std::pair<int, int> e, User u){ return e.first < u.getId(); });
^
D:\asant\workspace\CLionProjects\untitled\main.cpp:21:156: note: no known conversion for argument 1 from 'User' to 'std::pair<int, int>'
mingw32-make.exe[3]: *** [CMakeFiles\untitled.dir\build.make:82: CMakeFiles/untitled.dir/main.cpp.obj] Error 1
mingw32-make.exe[2]: *** [CMakeFiles\Makefile2:95: CMakeFiles/untitled.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:102: CMakeFiles/untitled.dir/rule] Error 2
mingw32-make.exe: *** [Makefile:137: untitled] Error 2
For a reason that is not known to me:
The types Type1 and Type2 must be such that objects of types InputIt1
and InputIt2 can be dereferenced and then implicitly converted to both
Type1 and Type2
See cppreference. Last sentence under Parameters -> comp. Highlighted by me.
The value type of the template class std::map is defined like
typedef pair<const Key, T> value_type;
However the value type of the class std::vector<int> is int.
You can not assign an object of the typs std::pair to an object of the type int.
So the compiler issues an error.
Moreover the comparison function can be called with any order of the passed arguments. So again the compiler can issue an error that invalid arguments are used because the types of parameters are different and there is no implicit conversion from one type to another..
You may want either roll own difference function or provide User with conversion operator which would allow set_difference to compare pair and User.
set_difference requires mutual conversion and ability to insert Type1 to output sequence. With use of std::map mutual conversion is not possible because conversion operator must be member of std::pair in that case.
The comparator cmp for set_difference is LessCompare and the equivalence will be checked with
something like:
pair<int, int> e == User u <==> !(e < u) && !(u < e) <==> !cmp(e,u) && !cmp(u,e)
This will work only if e and u are convertible to each other, which is not the case.
I believe your lambda is taking parameters in the wrong order. Try the one below.
[](const User &u, const std::pair<int, int> &e){ return u.getId() == e.first; });

Fill std::map with std::generate_n

I'd like to fill a std::map using std::generate_n but can't get it to work. What I tried is something along these lines:
unsigned number_of_pairs{5};
std::map<std::string, std::string> my_map;
auto read_pair_from_input = [](){
std::string key;
std::getline(std::cin, key);
std::string value;
std::getline(std::cin, value);
return std::make_pair(key, value);
};
std::generate_n(my_map.begin(), number_of_pairs, read_pair_from_input);
This gives me long errors like:
In file included from /opt/wandbox/gcc- head/include/c++/8.0.0/algorithm:62:0,
from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algo.h: In instantiation of '_OIter std::generate_n(_OIter, _Size, _Generator) [with _OIter = std::_Rb_tree_iterator<std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >; _Size = unsigned int; _Generator = main()::<lambda()>]':
prog.cc:18:74: required from here
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algo.h:4468:11: error: use of deleted function 'std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator= (typename std::conditional<std::__not_<std::__and_<std::is_copy_assignable<_Tp>, std::is_copy_assignable<_T2> > >::value, const std::pair<_T1, _T2>&, const std::__nonesuch_no_braces&>::type) [with _T1 = const std::__cxx11::basic_string<char>; _T2 = std::__cxx11::basic_string<char>; typename std::conditional<std::__not_<std::__and_<std::is_copy_assignable<_Tp>, std::is_copy_assignable<_T2> > >::value, const std::pair<_T1, _T2>&, const std::__nonesuch_no_braces&>::type = const std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&]'
*__first = __gen();
~~~~~~~~~^~~~~~~~~
In file included from /opt/wandbox/gcc-head/include/c++/8.0.0/utility:70:0,
from /opt/wandbox/gcc-head/include/c++/8.0.0/algorithm:60,
from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_pair.h:378:7: note: declared here
operator=(typename conditional<
^~~~~~~~
Is it possible to fill a std::map with std::generate_n?
What you want is a std::inserter:
std::generate_n(std::inserter(my_map, my_map.begin()), number_of_pairs, read_pair_from_input);
The inserter will wrap your map into an iterator-like construct that std::generate_n can use
Demo
std::generate_n can be implemented like
template< class OutputIt, class Size, class Generator >
OutputIt generate_n( OutputIt first, Size count, Generator g )
{
for( Size i = 0; i < count; i++ ) {
*first++ = g();
}
return first;
}
As you can see it tries to assign the result of the generator to the iterator. This does not work with associative containers as you cannot modify the key as that would break the structure of the container.
What you need is different type of iterator, namely a std::insert_iterator that you can get using std::inserter like
std::generate_n(std::inserter(my_map, my_map.begin()), number_of_pairs, read_pair_from_input);

fake iterator operations for enumerating all the node values in a graph

I'm trying to have a fake iterator for enumerating all the node values in a graph. The graph can be abstracted in any sequence, as long as the sequence consists of all and only the nodes in the graph. There should be a mutable qualifier, and the 4 operations' implementations are short and supposed to be only 1 line per function.
The test case for the 4 fake iterator operations is as follow, it is supposed to print out a string of values of the graph nodes through the fake iterator operations:
gdwg::Graph<std::string,int>> g
for (g.begin(); !g.end(); g.next())
std::cout << g.value() << std::end;
I declared the graph as the following:
template <typename N, typename E> class Graph {
private:
struct Node;
struct Edge;
struct Node {
N val_;
int numEdges_;
int numIncomingEdges_;
std::set<std::shared_ptr<Edge>> edges_;
std::set<std::shared_ptr<Edge>> incomingEdges_;
Node() {}
Node(const N x) : val_{x} { numEdges_=0; numIncomingEdges_=0; }
void printNode(N n);
~Node();
void update();
};
struct Edge {
std::weak_ptr<Node> orig;
std::weak_ptr<Node> dest;
E val_;
Edge(std::shared_ptr<Node> o, std::shared_ptr<Node> d, E x);
Edge() {};
void printEdge();
~Edge();
};
// fake iterator operations
void begin() const;
bool end() const;
void next() const;
const N& value() const;
public:
friend class Node_Iterator<N, E>;
friend class Edge_Iterator<N, E>;
private:
std::map< N, std::shared_ptr<Node> > nodes_;
// fake iterator to be used for the four operations begin(), end(), next() and value()
mutable typename std::map< N, std::shared_ptr<Node> >::iterator fakeIter_;
};
The implementation of the 4 operations are as follows:
// void begin() const: Sets an internal iterator, i.e., ``pointer'' to the first element of a sequence.
template <typename N, typename E>
void Graph<N,E>::begin() const {
// gets iterator to the first key/value pair in map sequence
fakeIter_ = nodes_.begin();
}
// bool end() const: Returns true if the iterator goes past the last element of the sequence and false otherwise.
template <typename N, typename E>
bool Graph<N,E>::end() const {
// return true if iterator goes past last element, otherwise return false
return ((fakeIter_ == nodes_.end()) ? true : false);
}
// void next() const: Moves the iterator to the next element of the sequence.
template <typename N, typename E>
void Graph<N,E>::next() const {
fakeIter_ = std::next(fakeIter_, 1);
}
// const N& value() const: Returns the value of the node pointed to by the iterator.
template <typename N, typename E>
const N& Graph<N,E>::value() const {
return fakeIter_->second->val_;
}
When I try to compile, there is a bunch of errors popping up. I'm just wondering whether I'm implementing the fake iterator operations correctly and if there is any way to improve it if need be?
tests/Graph.tem: In instantiation of ‘void gdwg::Graph<N, E>::begin() const [with N = unsigned int; E = int]’:
tests/test13.cpp:23:15: required from here
tests/Graph.tem:713:12: error: no match for ‘operator=’ (operand types are ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::iterator {aka std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’ and ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::const_iterator {aka std::_Rb_tree_const_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’)
fakeIter_ = nodes_.begin();
In file included from /usr/local/include/c++/6.1.0/map:60:0,
from tests/Graph.h:19,
from tests/test13.cpp:3:
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: candidate: constexpr std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >& std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >::operator=(const std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&)
struct _Rb_tree_iterator
^~~~~~~~~~~~~~~~~
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: no known conversion for argument 1 from ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::const_iterator {aka std::_Rb_tree_const_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’ to ‘const std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&’
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: candidate: constexpr std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >& std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >::operator=(std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&&)
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: no known conversion for argument 1 from ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::const_iterator {aka std::_Rb_tree_const_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’ to ‘std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&&’
What that first error is saying is that, as your begin function is const, nodes_.begin() will return a const_iterator. This cannot be assigned to an iterator, the type of fakeIter_.
You'll need to remove the const from begin, change the type of fakeIter_, or change your implementation (begin should return an iterator, not void).

BGL dfs from a set of source nodes

Problem
Having an adjacency list graph, I would like to traverse it with a DFS algorithm from a specific set of source nodes. The main problem is that the color map is passed by value.
I tried
To encapsulate the color map by reference into a structure :
class ref_color_map_wrapper
{
public:
typedef boost::default_color_type color_type;
typedef std::vector<color_type> color_map_type;
private:
color_map_type &color_map;
public:
ref_color_map_wrapper(color_map_type& color_map)
: color_map(color_map)
{}
color_type& operator[](size_t i)
{
return color_map[i];
}
const color_type& operator[](size_t i) const
{
return color_map[i];
}
};
namespace boost
{
template <>
struct property_traits<ref_color_map_wrapper>
{
typedef boost::read_write_property_map_tag category;
typedef boost::default_color_type value_type;
typedef boost::default_color_type& reference;
typedef size_t key_type;
};
void put(ref_color_map_wrapper& color_map, vertex_descriptor& v, boost::default_color_type color)
{
color_map[v] = color;
}
boost::default_color_type get(ref_color_map_wrapper& color_map, vertex_descriptor& v)
{
return color_map[v];
}
void put(ref_color_map_wrapper& color_map, const vertex_descriptor& v, boost::default_color_type color)
{
color_map[v] = color;
}
boost::default_color_type get(const ref_color_map_wrapper& color_map, const vertex_descriptor& v)
{
return color_map[v];
}
}
And finally the code of the DFS :
typedef std::vector<boost::default_color_type> color_map_type;
color_map_type color_map(boost::num_vertices(graph), boost::white_color);
ref_color_map_wrapper ref_color_map(color_map);
for(auto it = root_set.begin(); it != root_set.end(); ++it)
{
size_t i = boost::get(boost::vertex_index_t(), graph, *it);
if(color_map[i] == boost::white_color)
{
boost::depth_first_visit(graph, *it, boost::default_dfs_visitor(), ref_color_map);
}
}
Compilation error
/usr/local/include/boost/property_map/property_map.hpp: In instantiation of ‘void boost::ReadablePropertyMapConcept<PMap, Key>::constraints() [with PMap = gc::ref_color_map_wrapper; Key = long unsigned int]’:
/usr/local/include/boost/concept/detail/has_constraints.hpp:32:14: required by substitution of ‘template<class Model> boost::concepts::detail::yes boost::concepts::detail::has_constraints_(Model*, boost::concepts::detail::wrap_constraints<Model, (& Model:: constraints)>*) [with Model = boost::ReadablePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int>]’
/usr/local/include/boost/concept/detail/has_constraints.hpp:42:5: required from ‘const bool boost::concepts::not_satisfied<boost::ReadablePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int> >::value’
/usr/local/include/boost/concept/detail/has_constraints.hpp:45:31: required from ‘struct boost::concepts::not_satisfied<boost::ReadablePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int> >’
/usr/local/include/boost/mpl/if.hpp:67:11: required from ‘struct boost::mpl::if_<boost::concepts::not_satisfied<boost::ReadablePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int> >, boost::concepts::constraint<boost::ReadablePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int> >, boost::concepts::requirement<boost::concepts::failed************ boost::ReadablePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int>::************> >’
/usr/local/include/boost/concept/detail/general.hpp:50:8: required from ‘struct boost::concepts::requirement_<void (*)(boost::ReadablePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int>)>’
/usr/local/include/boost/concept_check.hpp:45:1: [ skipping 4 instantiation contexts ]
/usr/local/include/boost/concept/detail/has_constraints.hpp:45:31: required from ‘struct boost::concepts::not_satisfied<boost::ReadWritePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int> >’
/usr/local/include/boost/mpl/if.hpp:67:11: required from ‘struct boost::mpl::if_<boost::concepts::not_satisfied<boost::ReadWritePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int> >, boost::concepts::constraint<boost::ReadWritePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int> >, boost::concepts::requirement<boost::concepts::failed************ boost::ReadWritePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int>::************> >’
/usr/local/include/boost/concept/detail/general.hpp:50:8: required from ‘struct boost::concepts::requirement_<void (*)(boost::ReadWritePropertyMapConcept<gc::ref_color_map_wrapper, long unsigned int>)>’
/usr/local/include/boost/graph/depth_first_search.hpp:88:1: required from ‘void boost::detail::depth_first_visit_impl(const IncidenceGraph&, typename boost::graph_traits<Graph>::vertex_descriptor, DFSVisitor&, ColorMap, TerminatorFunc) [with IncidenceGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, boost::variant<const void*, std::pair<void*, void*> > >; DFSVisitor = boost::dfs_visitor<>; ColorMap = gc::ref_color_map_wrapper; TerminatorFunc = boost::detail::nontruth2; typename boost::graph_traits<Graph>::vertex_descriptor = long unsigned int]’
/usr/local/include/boost/graph/depth_first_search.hpp:314:5: required from ‘void boost::depth_first_visit(const IncidenceGraph&, typename boost::graph_traits<Graph>::vertex_descriptor, DFSVisitor, ColorMap) [with IncidenceGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, boost::variant<const void*, std::pair<void*, void*> > >; DFSVisitor = boost::dfs_visitor<>; ColorMap = gc::ref_color_map_wrapper; typename boost::graph_traits<Graph>::vertex_descriptor = long unsigned int]’
../include/garbage_collector.hpp:169:87: required from here
/usr/local/include/boost/property_map/property_map.hpp:200:7: error: no matching function for call to ‘get(gc::ref_color_map_wrapper&, long unsigned int&)’
/usr/local/include/boost/property_map/property_map.hpp:200:7: note: candidates are:
In file included from /usr/local/include/boost/tuple/tuple.hpp:33:0,
from /usr/local/include/boost/unordered/detail/allocate.hpp:27,
from /usr/local/include/boost/unordered/detail/buckets.hpp:15,
from /usr/local/include/boost/unordered/detail/table.hpp:10,
from /usr/local/include/boost/unordered/detail/equivalent.hpp:14,
from /usr/local/include/boost/unordered/unordered_set.hpp:17,
from /usr/local/include/boost/unordered_set.hpp:16,
from /usr/local/include/boost/graph/adjacency_list.hpp:21,
from ../include/garbage_collector.hpp:6,
from main.cpp:3:
/usr/local/include/boost/tuple/detail/tuple_basic.hpp:225:1: note: template<int N, class HT, class TT> typename boost::tuples::access_traits<typename boost::tuples::element<N, boost::tuples::cons<HT, TT> >::type>::const_type boost::tuples::get(const boost::tuples::cons<HT, TT>&)
/usr/local/include/boost/tuple/detail/tuple_basic.hpp:225:1: note: template argument deduction/substitution failed:
In file included from /usr/local/include/boost/graph/adjacency_list.hpp:36:0,
from ../include/garbage_collector.hpp:6,
from main.cpp:3:
/usr/local/include/boost/property_map/property_map.hpp:200:7: note: ‘gc::ref_color_map_wrapper’ is not derived from ‘const boost::tuples::cons<HT, TT>’
In file included from /usr/local/include/boost/tuple/tuple.hpp:33:0,
from /usr/local/include/boost/unordered/detail/allocate.hpp:27,
from /usr/local/include/boost/unordered/detail/buckets.hpp:15,
from /usr/local/include/boost/unordered/detail/table.hpp:10,
from /usr/local/include/boost/unordered/detail/equivalent.hpp:14,
from /usr/local/include/boost/unordered/unordered_set.hpp:17,
from /usr/local/include/boost/unordered_set.hpp:16,
from /usr/local/include/boost/graph/adjacency_list.hpp:21,
from ../include/garbage_collector.hpp:6,
from main.cpp:3:
/usr/local/include/boost/tuple/detail/tuple_basic.hpp:211:1: note: template<int N, class HT, class TT> typename boost::tuples::access_traits<typename boost::tuples::element<N, boost::tuples::cons<HT, TT> >::type>::non_const_type boost::tuples::get(boost::tuples::cons<HT, TT>&)
/usr/local/include/boost/tuple/detail/tuple_basic.hpp:211:1: note: template argument deduction/substitution failed:
In file included from /usr/local/include/boost/graph/adjacency_list.hpp:36:0,
from ../include/garbage_collector.hpp:6,
from main.cpp:3:
/usr/local/include/boost/property_map/property_map.hpp:200:7: note: ‘gc::ref_color_map_wrapper’ is not derived from ‘boost::tuples::cons<HT, TT>’
/usr/local/include/boost/property_map/property_map.hpp:179:19: note: template<class T> const T& get(const T*, std::ptrdiff_t)
/usr/local/include/boost/property_map/property_map.hpp:179:19: note: template argument deduction/substitution failed:
/usr/local/include/boost/property_map/property_map.hpp:200:7: note: mismatched types ‘const T*’ and ‘gc::ref_color_map_wrapper’
Graph definition
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, vertex_info_type> graph_type;
What's working
boost::get(ref_color_map, *it);
boost::put(ref_color_map, *it, boost::white_color);
works without any compilation error…
You need to put your get() and put() functions in the namespace where ref_color_map resides, since they are found via ADL. See here.

Why doesn't my custom iterator work with the STL copy?

I wrote an OutputIterator for an answer to another question. Here it is:
#include <queue>
using namespace std;
template< typename T, typename U >
class queue_inserter {
queue<T, U> &qu;
public:
queue_inserter(queue<T,U> &q) : qu(q) { }
queue_inserter<T,U> operator ++ (int) { return *this; }
queue_inserter<T,U> operator * () { return *this; }
void operator = (const T &val) { qu.push(val); }
};
template< typename T, typename U >
queue_inserter<T,U> make_queue_inserter(queue<T,U> &q) {
return queue_inserter<T,U>(q);
}
This works great for this little copy function:
template<typename II, typename OI>
void mycopy(II b, II e, OI oi) {
while (b != e) { *oi++ = *b++; }
}
But it doesn't work at all for the STL copy from algorithms. Here are the wonderful C++ errors I get:
i.cpp:33: error: specialization of ‘template<class _Iterator> struct std::iterator_traits’ in different namespace
/usr/include/c++/4.0.0/bits/stl_iterator_base_types.h:127: error: from definition of ‘template<class _Iterator> struct std::iterator_traits’
/usr/include/c++/4.0.0/bits/stl_algobase.h: In function ‘_OI std::__copy_aux(_II, _II, _OI) [with _II = int*, _OI = queue_inserter<int, std::deque<int, std::allocator<int> > >]’:
/usr/include/c++/4.0.0/bits/stl_algobase.h:335: instantiated from ‘static _OI std::__copy_normal<true, false>::copy_n(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OI = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
/usr/include/c++/4.0.0/bits/stl_algobase.h:387: instantiated from ‘_OutputIterator std::copy(_InputIterator, _InputIterator, _OutputIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OutputIterator = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
i.cpp:53: instantiated from here
/usr/include/c++/4.0.0/bits/stl_algobase.h:310: error: no type named ‘value_type’ in ‘struct std::iterator_traits<queue_inserter<int, std::deque<int, std::allocator<int> > > >’
/usr/include/c++/4.0.0/bits/stl_algobase.h:315: error: no type named ‘value_type’ in ‘struct std::iterator_traits<queue_inserter<int, std::deque<int, std::allocator<int> > > >’
/usr/include/c++/4.0.0/bits/stl_algobase.h:315: error: ‘__value’ is not a member of ‘<declaration error>’
/usr/include/c++/4.0.0/bits/stl_algobase.h:335: instantiated from ‘static _OI std::__copy_normal<true, false>::copy_n(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OI = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
/usr/include/c++/4.0.0/bits/stl_algobase.h:387: instantiated from ‘_OutputIterator std::copy(_InputIterator, _InputIterator, _OutputIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OutputIterator = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
i.cpp:53: instantiated from here
/usr/include/c++/4.0.0/bits/stl_algobase.h:317: error: ‘__simple’ is not a valid template argument for type ‘bool’ because it is a non-constant expression
/usr/include/c++/4.0.0/bits/stl_algobase.h:317: error: ‘copy’ is not a member of ‘<declaration error>’
Here is the driver:
int main() {
vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
queue<int> q;
copy( v.begin(), v.end(), make_queue_inserter(q) );
while (q.size() > 0) {
cout << q.front() << endl;
q.pop();
}
}
Why in the world is it specializing iterator_traits. What's wrong with my iterator? Can't I just write my own simple iterators?
Your queue_inserter needs to be derived from std::iterator so that all the typedefs such as value_type are properly defined since these are used inside STL algorithms This definition works:
template< typename T, typename U >
class queue_inserter : public std::iterator<std::output_iterator_tag, T>{
queue<T, U> &qu;
public:
queue_inserter(queue<T,U> &q) : qu(q) { }
queue_inserter<T,U> operator ++ (int) { return *this; }
queue_inserter<T,U> operator ++ () { return *this; }
queue_inserter<T,U> operator * () { return *this; }
void operator = (const T &val) { qu.push(val); }
};
Derive it from std::iterator. If you are interested the Dr. Dobb's has an article about custom containers and iterators.
Your iterator doesn't meet the requirement for an 'assignable' type which is a requirement for an output iterator because it contains a reference and assignable types need to ensure that after t = u that t is equivalent to u.
You can provide a suitable specialization for iterator_traits for your iterator either by deriving from a specialization of std::iterator or by providing one explicitly.
namespace std
{
template<> struct iterator_traits<MyIterator>
{
typedef std::output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
};
}
#include <queue>
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace std;
template< typename T, typename U >
class queue_inserter
{
queue<T, U> &qu;
public:
// for iterator_traits to refer
typedef output_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
queue_inserter(queue<T,U> &q) : qu(q) { }
queue_inserter<T,U>& operator ++ () { return *this; }
queue_inserter<T,U> operator * () { return *this; }
void operator = (const T &val) { qu.push(val); }
};
template< typename T, typename U >
queue_inserter<T,U> make_queue_inserter(queue<T,U> &q)
{
return queue_inserter<T,U>(q);
}
int main()
{
// uses initalizer list (C++0x), pass -std=c++0x to g++
vector<int> v({1, 2, 3});
queue<int, deque<int>> q;
copy(v.cbegin(), v.cend(), make_queue_inserter(q));
while (!q.empty())
{
cout << q.front() << endl;
q.pop();
}
}
This should do it with iterator_traits; a helper struct in <iterator> which defines all types an iterator should typically define. Functions in <algorithm>, refer to these types when required like iterator_traits<it>::iterator_category or say iterator_traits<it>::value_type, etc. Just defining them inside one's custom iterator would do the trick. This is the modern way of writing iterators, as opposed to the classical way of inheriting from std::iterator. Having a look at <iterator> reveals that even std::iterator defines these types i.e. iterator_category, difference_type, etc. This is the reason, when inherited from std::iterator, the derived iterator class gets these due to heredity.