How to print a map - c++

I am trying to print a map in an organized way. My map is defined like this:
map<std::string,std::vector<message *> > data;
where message is a struct like this:
struct message{
static unsigned int last_id;
unsigned int id;
std::string msg;
std::string timestamp;
message(const std::string& recvbuf_msg,const std::string& a_timestamp) :
msg(recvbuf_msg), timestamp(a_timestamp), id(++last_id)
{
}
};
I tried this way of printing it:
std::cout << (data[username]).at(0)->msg << std::endl;
But it gives a debug error when reaching that function, how can i solve it?

Error R6010 - abort() has been called suggests that either there is no entry for key username in the map, or the vector of messages for that user is empty. You need to make sure the containers are nonempty before accessing elements. It is a good idea to use iterators, here is an example of how to print the messages for all usernames:
for(auto mapIt = data.cbegin(); mapIt != data.cend(); ++mapIt)
{
std::cout << "printing data for " << mapIt->first << ":" << std::endl;
for(auto vectIter = mapIt->second.cbegin(); vectIter != mapIt->second.cend(); ++vectIter)
{
std::cout << (*vectIter)->msg << ", " << (*vectIter)->timestamp << ", "
<< (*vectIter)->id << std::endl;
}
}
The code uses auto, so if you are not using a C++11 compliant compiler, you will have to write the iterator types yourself.

Related

C++ using vector iterator correctly

I'm new to C++ and I have a vector of doctors.
I add a new doctor with the following code:
void DoctorAdmin::setDoctor(std::string lastname, std::string forename,
Person::Sex sex){
//Create new doctor
Doctor* doc = new Doctor(lastname, forename, sex);
//insert at the end of the vector
doctors.push_back(doc);
}
Then I want to show their information on the console:
void DoctorAdmin::showDoctors(){
cout << "Doctors:" << endl;
cout << "Name" << "\t\t\t" << "Forename" << "\t\t\t" << "Sex" << endl;
for (vector<Doctor*>::iterator i = doctors.begin(); i != doctors.end(); i++){
Doctors* doc = doctors.at(i);
cout << doc->getName() << "\t\t\t" << doc->getForename() << "\t\t\t"
<< doc->getSex() << endl;
}
After doing it like this I get two Errors:
E0304 No instance of overloaded function "std::vector<_Ty, _Alloc>::at [mit _Ty=Doctors *, _Alloc=std::allocator<Doctors *>]" matches the argument list.
// and
C2664 "Doctors *const &std::vector<Doctors *,std::allocator<_Ty>>::at(const unsigned int) const" : cannot convert from Argument "std::_Vector_iterator<std::_Vector_val<std::_Simple_types<_Ty>>>" in "const unsigned int"
How do I use the vector iterator correctly to avoid this?
An iterator is not index-like, it is pointer-like.
for (vector<Arzt*>::iterator doc = aerzte.begin(); doc != aerzte.end(); doc++)
{
cout << (*doc)->getName() << "\t\t\t" << (*doc)->getVorname() << "\t\t\t"
<< (*doc)->getGeschlecht() << endl;
}
It seems like you are confused as to when you need to new things too. Most of the time you don't need new
vector<Arzt> aerzte;
void ArztAdmin::anlegenArzt(std::string name, std::string vorname, Person::Geschlecht geschlecht){
// Create new doctor at the end of the vector
aerzte.emplace_back(name, vorname, geschlecht);
}
You can also directly bind references as loop variables
for (Arzt & doc : aerzte)
{
cout << doc.getName() << "\t\t\t" << doc.getVorname() << "\t\t\t"
<< doc.getGeschlecht() << endl;
}
The at function requires an index, but a vector<Arzt*>::iterator is not an index, neither semantically nor technically. An iterator points directly to an element, whereas an index represents the distance between a container's start and the element in a container that allows random element access.
Because an iterator points directly to an element, the at function isn't even necessary in your loop. *i yields the element:
Arzt* doc = *i;
Beginning with C++11, the code for such simple loops can be written in a shorter way using auto:
for (auto i = aerzte.begin(); i != aerzte.end(); i++){
The compiler knows what type i really is because it knows what begin() returns.
Even better, use a range-based loop:
for (auto doc : aerzte){
cout << doc->getName() << "\t\t\t" << doc->getVorname() << "\t\t\t"
<< doc->getGeschlecht() << endl;
}
And while we're at it, don't use dynamic memory allocation when you don't have to. This isn't Java or C#; new is dangerous territory in C++ and should be avoided:
#include <vector>
#include <string>
#include <iostream>
struct Arzt
{
Arzt(std::string const& name, std::string const& vorname) :
name(name),
vorname(vorname)
{
}
std::string name;
std::string vorname;
// Geschlecht omitted for brevity's sake
};
int main()
{
std::vector<Arzt> aerzte;
Arzt doc1("foo", "bar");
Arzt doc2("foo", "bar");
Arzt doc3("foo", "bar");
aerzte.push_back(doc1);
aerzte.push_back(doc2);
aerzte.push_back(doc3);
for (auto const& arzt : aerzte)
{
std::cout << arzt.name << ' ' << arzt.vorname << '\n';
}
}
As you are no longer iterating over pointers but over larger objects, const& should be used in the for loop.

Moving objects from one unordered_map to another container

My question is that of safety. I've searched cplusplus.com and cppreference.com and they seem to be lacking on iterator safety during std::move. Specifically: is it safe to call std::unordered_map::erase(iterator) with an iterator whose object has been moved? Sample code:
#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
class A {
public:
A() : name("default ctored"), value(-1) {}
A(const std::string& name, int value) : name(name), value(value) { }
std::string name;
int value;
};
typedef std::shared_ptr<const A> ConstAPtr;
int main(int argc, char **argv) {
// containers keyed by shared_ptr are keyed by the raw pointer address
std::unordered_map<ConstAPtr, int> valued_objects;
for ( int i = 0; i < 10; ++i ) {
// creates 5 objects named "name 0", and 5 named "name 1"
std::string name("name ");
name += std::to_string(i % 2);
valued_objects[std::make_shared<A>(std::move(name), i)] = i * 5;
}
// Later somewhere else we need to transform the map to be keyed differently
// while retaining the values for each object
typedef std::pair<ConstAPtr, int> ObjValue;
std::unordered_map<std::string, std::vector<ObjValue> > named_objects;
std::cout << "moving..." << std::endl;
// No increment since we're using .erase() and don't want to skip objects.
for ( auto it = valued_objects.begin(); it != valued_objects.end(); ) {
std::cout << it->first->name << "\t" << it->first.value << "\t" << it->second << std::endl;
// Get named_vec.
std::vector<ObjValue>& v = named_objects[it->first->name];
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
// And then... is this also safe???
it = valued_objects.erase(it);
}
std::cout << "checking... " << named_objects.size() << std::endl;
for ( auto it = named_objects.begin(); it != named_objects.end(); ++it ) {
std::cout << it->first << " (" << it->second.size() << ")" << std::endl;
for ( auto pair : it->second ) {
std::cout << "\t" << pair.first->name << "\t" << pair.first->value << "\t" << pair.second << std::endl;
}
}
std::cout << "double check... " << valued_objects.size() << std::endl;
for ( auto it : valued_objects ) {
std::cout << it.first->name << " (" << it.second << ")" << std::endl;
}
return 0;
}
The reason I ask is that it strikes me that moving the pair from the unordered_map's iterator may (?) therefore *re*move the iterator's stored key value and therefore invalidate its hash; therefore any operations on it afterward could result in undefined behavior. Unless that's not so?
I do think it's worth noting that the above appears to successfully work as intended in GCC 4.8.2 so I'm looking to see if I missed documentation supporting or explicitly not supporting the behavior.
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
Yes, it is safe, because this doesn't actually modify the key. It cannot, because the key is const. The type of *it is std::pair<const ConstAPtr, int>. When it is moved, the first member (the const ConstAPtr) is not actually moved. It is converted to an r-value by std::move, and becomes const ConstAPtr&&. But that doesn't match the move constructor, which expects a non-const ConstAPtr&&. So the copy constructor is called instead.

Accessing one "item" of a map

I wanted to access one "item" of my map<wstring,wstring>.
But this does not work:
for (unsigned int i=0;i<m_ValidCharacterTranslations.Content().size();i++)
{
wstring wsFirst = &m_ValidCharacterTranslations.Content()[i]->first;
wstring wsSecond = &m_ValidCharacterTranslations.Content().at(i)->second;
//do something with these 2 wstrings
}
The error I am getting in the last line is:
No binary operator accepts the right-handed operand of type 'unsigned int'.
My class is declared like this:
clsTranslations m_ValidCharacterTranslations;
class clsTranslations : public CBaseStructure
{
private:
map<wstring,wstring> m_content;
protected:
virtual void ProcessTxtLine(string line);
public:
map<wstring,wstring> &Content();
void LoadTranslations(string file);
};
Can somebody tell me how to get these values?
I would like to iterate through the map and use the first and the
second wstring of the map.
C++11:
for (auto& kvpair : somemap) {
cout << kvpair.first << " has value " << kvpair.second << std::endl;
}
pre C++11:
for (map<wstring,wstring>::iterator it = somemap.begin(); it != somemap.end(); it++) {
cout << it->first << " has value " << it->second << std::endl;
}
You access first and second addresses, instead of values.

How to print a map? And make it a global variable? C++

For Windows 7 64-bit
So I set up my map
map<string, string> database;
database["user"] = "123";
It's part of the main function, but how can I print the contents of the map? And most importantly how can it be turned into a global variable so I can use it by other functions? I'm trying to put my printing map process in a different function than where the map was made.
You can iterate through map with following code:
for(auto it = database.begin(); it != database.end(); ++it) {
// it->first is your key
// it->second is value of particular key
std::cout << "Key: " << it->first << std::endl;
std::cout << "Value: " << it->second << std::endl;
// value can be reached as follows as well
std::cout << "Value: " << database[it->first] << std::endl;
}
Try this:
#include <map>
#include <iostream>
// this will be a global variable
map<string, string> database;
int main()
{
database["user"] = "123";
map<string, string>::iterator it;
for(it = database.begin(); it!= database.end(); it++)
{
pair<string, string> p = *it;
cout << "key=\"" << p.first << "\" value=\"" << p.second << "\"" << endl;
}
}
I'd advise to use C++11 features, like for_each, range based begin and end and lambda:
std::for_each(std::begin(database), std::end(database),
[&database](std::pair<std::string, std::string> p) {
std::cout << "Key : " << p.first << "\tValue : " << p.second << std::endl;
});
Second I'd avoid to make your map a global variable. There are reasons for global definitions, but for a container? (This maybe depends how big your project is, but I'd say let's get rid of any global variable, and if it is really needed put it inside a namespace)
Why not encapsulate your map into a class and provide functions to alter the content of your map, even your printing facility? If you want to make sure, that every user of your class shall have the same content of the map (translated this means the same instance), you can turn your class into a singleton.
If you have C++1 available (and as a sidenote to Stefan's answer: You don't actually need std::for_each or lambdas in C++11), iteration can be done using range based for:
for ( std::pair<std::string,std::string> const &p : database)
std::cout << "key=\"" << p.first << "\" value=\"" << p.second << "\"" << std::endl;
If you want to use it in other functions, think about several things instead of making the variable global:
Aggreate/incapsulate all functions using the database in a class
Pass the map object to your functions

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;
}
// ...