Differences between std::set and boost::ptr_set? - c++

I've changed some code to convert a std::set to a boost::ptr_set. However, the code doesn't compile; the problem is that I'm assuming that the return value from a ptr_set insert is the same as a set insert (a pair<myIter, bool>). After an hour on Google I found this, and it turns out that the return value from a ptr_set insert appears to be a bool.
Is there any definitive documentation on the differences between the ptr containers and the std containers? I haven't found anything on the boost website, but maybe I'm just being dumb...
EDIT
Ok - what was confusing me was that this code
t.insert(s.release(s.begin()));
p = t.insert(s.release(s.begin()));
reports no error on the first line on gcc, but reports no match for operator= on the second line, so I thought the error was in the return type. However, if you comment out the second line, the first line is then reported as an error (release doesn't return an iterator). My confusion was compounded by the link I posted, in which ptr_container's author states that "insert() in ptr_set<> returns bool". However, reading on down the link it becomes obvious that the code hadn't been finished at the time. Thanks Kerrek.

The following code works as expected, and the interface is the same as for std::set::insert():
#include <boost/ptr_container/ptr_set.hpp>
#include <boost/assign/ptr_list_inserter.hpp>
#include <iostream>
int main()
{
boost::ptr_set<int> s;
{
auto p = s.insert(new int(4));
std::cout << "Element " << *p.first << (p.second ? " inserted" : " already existed") << std::endl;
}
{
auto p = s.insert(new int(4));
std::cout << "Element " << *p.first << (p.second ? " inserted" : " already existed") << std::endl;
}
boost::assign::ptr_insert(s)(1)(2)(3)(4);
for (auto it = s.begin(), end = s.end(); it != end; ++it) { std::cout << *it << "\n"; }
}
The documentation is perhaps not the easiest to navigate, but it's all there. You should look for the "set adapter", though, perhaps that's not entirely obvious.

Related

Effectively getting items from map based on specific sort

I have a fairly easy problem: I have an std::map<int,T> and another std::set<int> (can be std::vector or similar too).
In the map I store items, and in the other container I'm storing favorites (of the map).
At some point, I'd need to retrieve (all) items from the map, but starting with the favorites defined by the other container.
Here is my minimal repro, I solved it very ugly, and ineffective:
#include <iostream>
#include <string>
#include <set>
#include <map>
using namespace std;
map<int, string> myMap;
set<int> myFavorites;
int main()
{
myMap.emplace(1, "but I don't like this");
myMap.emplace(12, "So it will go below");
myMap.emplace(31, "This one will come first, and");
myMap.emplace(44, "under my favorites");
myMap.emplace(52, "then this will follow");
myFavorites.insert(52);
myFavorites.insert(31);
cout << "My map:" << endl;
for(auto p : myMap) {
cout << "#" << p.first << "=" << p.second << endl;
}
cout << endl << "My favorites:" << endl;
for(auto p : myFavorites) {
cout << "#" << p << endl;
}
cout << endl << "All items starting with my favorites:" << endl;
for(auto p : myFavorites) {
auto item = myMap.find(p);
if (item != myMap.end()) cout << "#" << item->first << "=" << item->second << endl;
}
for(auto p : myMap) {
if (myFavorites.find(p.first) != myFavorites.end()) continue;
cout << "#" << p.first << "=" << p.second << endl;
}
}
What really bothers me is the last loop, where each iterations would call find on the set.
Required output is:
All items starting with my favorites:
#31=This one will come first, and
#52=then this will follow
#1=but I don't like this
#12=So it will go below
#44=under my favorites
Here is the above source in Coliru for making it easier: https://coliru.stacked-crooked.com/a/731fa76d90bfab00
Both map and set might be changed, but replacements needs to implement the same interfaces as originals.
I'm looking for a way to solve this more efficient than my original "brute-force" one.
Please note: map must not be "reordered"! I just need to query (retrieve) its items with custom sorting!
Note2: I know map can have a comparison operator. But I'd need to have the original order usually, and sometimes I'd need to have the custom sort!
Note3: Boost is not available and compiler is C++14 capable.
Both std::map and std::set use the same strict weak ordering for ordering its contents.
You can take advantage of this. You know that if you iterate over the map you will get the keys in the same order as they are in the set, therefore all it takes is a little bit of clever logic, something like:
auto map_iter=myMap.begin();
for(auto p : myFavorites) {
while (map_iter != myMap.end())
{
if (map_iter->first == p)
cout << "#" << map_iter->first << "=" << map_iter->second << endl;
if (map_iter->first > p)
break;
++map_iter;
}
}
It may still make sense to use find() in some edge cases, specifically when myFavorites is significantly smaller than myMap, in which case a few calls to find() might be faster than iterating over (most of) the entire map.

Dereferencing iterator behavior in C++

I'm trying to figure out why dereferencing the empty list iterator is preventing the rest of the code from running. Comment out the line and everything seems fine, but leave it in and the program doesn't seem to get past that point.
I guess it's supposed to be an error since the list is empty, but I'm not getting any warnings or errors.
I'm using codeblocks with MinGW
std::list<std::string> slist;
std::string word;
auto iter = slist.begin();
//what is this doing?
std::cout << (*iter) << std::endl;
while(std::cin >> word)
{
iter = slist.insert(iter, word);
}
slist.insert(slist.begin(), {"foo", "bar"});
for(auto item: slist)
std::cout << item << std::endl;
Well std::list is empty! De-referencing means you are attempting to use something that is not defined. It is just wrong. You should definitely not do that.
You should do instead
for (auto i : slist)
std::cout << i << std::endl;
which is safe.

How do I access the index & object from a boost::container::vector<std::string>::iterator?

I'm a boost newbie.
How do I access the object from an iterator? I have something like:
boost::container::vector<std::string>::iterator plitr = myvec.begin();
while (plitr != myvec.end()){
std::cout << "data at index[" << plitr - myvec.begin() << "]: " << plitr->x <<std::endl;
plitr++;
}
But I realize that plitr->x does not exist nor am I sure if the index can be calculated the way I think.
Can anyone help?
The usage of boost::vector is identical to std::vector. Calculating the index hence works the way you showed, because the iterator fulfills random access criteria. Concerning access to the object, you want to dereference the iterator. Change your loop to
while (plitr != myvec.end()){
std::cout << "data at index[" << plitr - myvec.begin() << "]: " << *plitr <<std::endl;
plitr++;
}
and it will work (note the *plitr, that't the dereferencing part). Just as a side note, using a range based for loop to access every std::string in myvec might be more convenient here:
for (auto&& str : myvec)
std::cout << str << std::endl;

how to iterate through boost unordered_map?

I just want to iterate through the members of an unordered map.
There are many simple examples on the web, including on this site, and yet none of them will compile. Apparently some examples are from a previous non-standard STL version, some are just old, and some are so new that my gcc 4.7.2 can't handle them. Please do not suggest the new auto iterator from C++11. I will get there some day when all my libraries are validated for that. Until then, I just want the old one to work. (see below for what I have tried)
Here is my test code:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[]) {
boost::unordered::unordered_map<std::string,int> umap;
//can't get gcc to accept the value_type()...
//umap.insert(boost::unordered_map::value_type("alpha",1));
//umap.insert(boost::unordered_map::value_type("beta",2));
//umap.insert(boost::unordered_map::value_type("gamma",3));
umap["alpha"]=1; //this works
umap["beta"]=2;
umap["gamma"]=3;
//can't get gcc to compile the iterator
//for (boost::unordered_map::iterator it=umap.begin();it!=umap.end();++it)
// std::cout << it->first <<", " << it->second << std::endl;
//gcc does not like it this way either
//for (int x=0;x<umap.size();x++)
// std::cout << x << " : " << umap[x].first << " = " << umap[x].second << std::endl;
//will gcc take this? No it does not
//for (int x=0;x<umap.size();x++)
// std::cout << x << " : " << umap[x] << std::endl;
//this does not work
//boost::unordered::unordered_map::iterator<std::string,int> it;
//this does not work
//boost::unordered::unordered_map::iterator it;
//for (it=umap.begin();it!=umap.end();++it)
// std::cout << it->first <<", " << it->second << std::endl;
//this does not work
//BOOST_FOREACH(boost::unordered_map::value_type value, umap) {
// std::cout << value.second;
// }
//std::cout << std::endl;
//this does not work either
//BOOST_FOREACH(boost::unordered_map::value_type<std::string,int> value, umap) {
// std::cout << value.second;
// }
//std::cout << std::endl;
std::cout << "umap size: " << umap.size() << std::endl;
std::cout << "umap max size: " << umap.max_size() << std::endl;
std::cout << "find alpha: " << (umap.find("alpha")!=umap.end()) << std::endl;
std::cout << "count beta: " << umap.count("beta") << std::endl;
}
Most of the errors are a variation of this:
error: 'template<class K, class T, class H, class P, class A> class boost::unordered::unordered_map' used without template parameters
Here is my build command:
g++ -I..\boost umap.cpp
I should be embarrassed for getting stuck on such a beginner's question, but from the volume of similar questions I am finding, this is just hard enough to stop a lot of people in their tracks. I have written hash containers before (back when it was recommended NOT to use STL) and I am very tempted to just write my own... but the right thing to do is learn to use as many existing tools as possible... help!
I've looked at the following questions on stackoverflow where I haven't found an answer:
iterate through unordered_map using boost_foreach
I tried:
BOOST_FOREACH(boost::unordered_map::value_type& value, umap) {
but it gives the same error I show below.
Unordered_map iterator invalidation
This one is close, but not quite my issue:
Iterator invalidation in boost::unordered_map
This one uses auto
and I can't switch compilers at this time.
C++ some questions on boost::unordered_map & boost::hash
This one is mostly about the theory of maps:
how to use boost::unordered_map
This is a rather complicated example, but you will see in my code I am already trying to declare iterators... they just won't compile.
How to use BOOST_FOREACH with an Unordered_map?
This is a nice example, but
it just does not compile. I tried a version of this in my code.
IT WORKS !
Here is the working code:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[]) {
boost::unordered::unordered_map<std::string,int> umap;
umap["alpha"]=1;
umap["beta"]=2;
umap["gamma"]=3;
boost::unordered::unordered_map<std::string,int>::iterator it;
for (it=umap.begin();it!=umap.end();++it)
std::cout << it->first <<", " << it->second << std::endl;
std::cout << "umap size: " << umap.size() << std::endl;
std::cout << "umap max size: " << umap.max_size() << std::endl;
std::cout << "find alpha: " << (umap.find("alpha")!=umap.end()) << std::endl;
std::cout << "count beta: " << umap.count("beta") << std::endl;
}
It was a syntax error. I was putting the type in the wrong place when declaring the iterator.
Thanks everyone for your responses.
try changing boost::unordered::unordered_map::iterator it; it to boost::unordered::unordered_map<std::string,int>::iterator it;
NOTE:
It is also possible, and a good idea in more complex situations, to create a typedef of it, such as typedef boost::unordered::unordered_map<std::string,int>::iterator UMapStringIntIt;, or whatever you may call it.
The answer is in the question, but the simple solution is here for your convenience:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[])
{
boost::unordered::unordered_map<std::string,int> umap;
umap["alpha"]=1;
umap["beta"]=2;
umap["gamma"]=3;
for ( auto it= umap.begin(); it != umap.end(); ++it )
std::cout << it->first <<", " << it->second << std::endl;
}

C++ STL map with custom comparator storing null pointers

I'm trying to write a copy constructor for an object managing a STL map containing pointers, where the key is a string. However, when I attempt to insert new values in the map, the pointers are set to NULL:
// ...
for(std::map<std::string, data_base*, order>::const_iterator it = other.elements.begin();
it != other.elements.end(); ++it){
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
elements[it->first] = t;
std::cout << "INSERTED: " << std::hex << elements[it->first] << std::endl;
}
// ...
other is the object being copied and elements the map. The clone() method returns a pointer to a new object (via new).
Running the code above I get something like:
CLONE: 0xcfbbc0
INSERTED: 0
I'm not a very experienced programmer and this issue is probably simple to fix, but I didnt find any solution to it searching around.
Thanks a lot for your time.
I don't see any problem with this code, other than maybe
std::map<std::string, data_base*, order>::const_iterator it
Here order gives the key comparator to use to sort the pairs contained in the map (often implemented as a tree).
Maybe you're doing something wrong in it, making your [] operator don't find the right ke, making your last line logging a new pair with a null ptr.
First, try without that order, using the default key-comparator (std::less), then if it don't work, post your order definition and the map declaration. If it's not enough, just provide a simple complete program that reproduce the problem.
I just wrote a simple similar test, using the default key-comparator :
#include <map>
#include <string>
#include <iostream>
struct Data
{
int k;
Data* clone() { return new Data(); }
};
typedef std::map< std::string, Data* > DataMap;
DataMap data_map;
int main()
{
data_map[ "hello" ] = new Data();
data_map[ "world" ] = new Data();
DataMap other_map;
for( DataMap::const_iterator it = data_map.begin(); it != data_map.end(); ++it)
{
Data*t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
other_map[it->first] = t;
std::cout << "INSERTED: " << std::hex << other_map[it->first] << std::endl;
}
std::cin.ignore();
return 0;
}
On VS2010SP1, this outputs :
CLONE: 00034DD0
INSERTED: 00034DD0
CLONE: 00035098
INSERTED: 00035098
So it should be the problem, or maybe you're doing something wrong before.
Try this out, to help debug the issue. I'd recommend double-checking that the order function is correct. You can remove it to use std::less<T>, which is known to work.
// ...
typedef std::map<std::string, data_base*, order> string_db_map;
for(string_db_map::const_iterator it = other.elements.begin();
it != other.elements.end();
++it)
{
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
std::pair<string_db_map::iterator, bool) result = elements.insert(
string_db_map::value_type( it->first, t));
if ( !result.second )
{
std::cout << "element['" << it->first << "'] was already present, and replaced." << std::endl;
}
std::coud << "INSERTED [iterator]: " << std::hex << (*result.first).second << std::endl;
std::cout << "INSERTED [indexed]: " << std::hex << elements[it->first] << std::endl;
}
// ...