Losing fields when filling C++ std::map - c++

I have a problem in using the std::map correctly. The class Example is a class with an ID, a label, a vector of keypoints and a descriptor matrix. The class Examples is a map for retrieving an example given its ID. The examples are read from files on disk, stored in the map, then used later.
Even if it is conceptually very simple, I am not able to fill the map properly.
I have the following class:
class Example
{
public:
std::string id;
std::string label;
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
Example(std::string id_, std::string label_)
: id(id_), label(label_)
{
// ... nothing ...
}
string to_string() const
{
stringstream ss;
ss << "#" << id
<< " (" << label << ")"
<< " - #keypoints " << keypoints.size()
<< ", descr " << descriptors.rows << " x " << descriptors.cols;
return ss.str();
} // to_string
}; // class Example
ostream& operator <<(ostream & out, const Example &ex)
{
out << ex.to_string();
return out;
} // operator <<
And this one:
// OLD: class Examples : public std::map<std::string, Example*> {
class Examples {
// New line after Martini's comment
std::map<std::string, Example*> _map;
[...]
void fill() {
// create an example
Example *example = new Example(id, label);
// inputstream in
// Read all the keypoints
cv::KeyPoint p;
for(int i=0; ... ) {
in.read(reinterpret_cast<char *>(&p), sizeof(cv::KeyPoint));
example->keypoints.push_back(p); // push_back copies p
} // for
// ... misc code
cv::Mat descr(n_keypoints, d_size, cv_type, cv::Scalar(1));
// ... read Mat from inputstream in, then assign it to the example
example->descriptors = descr;
// SEE THE OUTPUT BELOW
std::cout << "INSERT THIS: " << (*example) << std::endl;
_map.insert(std::pair<string,Example*>(id, example));
std::cout << "READ THIS: " << *(get_example(id)) << std::endl;
// ... other code
} // fill
// Code modified after Martini's comment.
Example* get_example(const std::string &id) const {
std::map<std::string, Example*>::const_iterator it = _map.find(id);
if( it == _map.end()) {
// ... manage error
// ... then exit
} // if
return it->second;
} // get_example
} // class Examples
The output from the insert/get lines is:
INSERT THIS: #122225084 (label) - #keypoints 711, descr 711 x 128
READ THIS: #122225084 (label) - #keypoints 0, descr 0 x 0
In the insert I had a pointer to an example with 711 keypoints and a 711x128 descriptor matrix. If I read the example using its ID right after the insert, I get a pointer to an example with 0 keypoints and an empty matrix.
What am I doing wrong?

Looking into your code one possible explanation is that you already have element in the map with the same key. To diagnose that first of all print value of pointer before you add object and after that (something like this):
std::cout << "INSERT THIS: " << (void *)example << " " << (*example) << std::endl;
_map.insert(std::pair<string,Example*>(id, example));
std::cout << "READ THIS: " << (void *)get_example(id) << " " << *(get_example(id)) << std::endl;
Next or another way is to check result of insert:
if( !_map.insert(std::pair<string,Example*>(id, example)).second )
std::cout << "ERROR: example:" << id << " is already there";
If you want to override element unconditionally you can use oprator[]:
_map[ id ] = example;
If there are really duplicates you will get memory leak (you are getting it anyway) so I would strongly recommend to use smart pointer to store data in your map.

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.

How to print a map

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.

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.

c++ private member variable unknown in another function

I have a newbie question about how to assign class member (setter). I am used to scripting and mostly there it's done via (in python)
def set_mymember(mymember):
self.mymeber = mymember
My coworker told me "self" and "this" are not needed in C++ , "this" exists and it's not wrong in this context but that would be hard to understand for me so he said I should not care. So I first tried according to his advice:
My class definition: - (it should create a sql query string)
class Query
{
public:
Query() { }
~Query() { }
void setoptions( std::string qtext_where="", bool qtext_erl=true, std::vector<std::string> kids=std::vector<std::string>() );
Query build_query( );
void set_db_table( std::string db_table );
void set_db_key( std::string db_key );
void set_m_qtext( std::string m_qtext );
void set_foo( std::string foo );
std::string sql();
std::string get_sql_update();
private:
std::string m_db_table; // Tabellenname
std::string m_db_key; // Tabellen-key
std::string m_qtext_where; // add.optionale where clause
std::string m_qtext; // fertiger SELECT
std::string m_sql_update; // fertiger UPDATE
bool m_erl; // nur erledigte waehlen?
std::vector<std::string> m_kids; // Liste von keys zu selecten
};
ANd here's one of the setter methods: I call them with filled string and vector, double check it in this code
void Query::setoptions( string qtext_where, bool erl, vector<string> kids ) {
m_qtext_where = qtext_where;
m_erl = erl;
m_kids = kids;
}
But when my app later calls query.build_query()
the variables are empty
Query Query::build_query( ) {
cout << "kids size" << m_kids.size() << endl;
cout << "m_qtext_where " << m_qtext_where << endl;
// Query zur auswahl der zu uebertragenden Datensaetze
string sql_update = "UPDATE " + m_db_table;
string qtext = "SELECT * FROM " + m_db_table;
string qtext_order = " ORDER BY " + m_db_key;
(...)
EDIT: So here's part of the app code which calls 1.setoptions, and 2.build_query
// read file line by line into vector of strings
vector<string> text_file;
ifstream ifs( input );
string temp;
while( getline( ifs, temp ) ) {
if (temp.substr(0,1) == "#" ) {
cout << "COMMENT: " << temp << endl;
continue;
}
cout << temp << endl;
text_file.push_back( temp );
}
// check: yes, vector has a size = number of lines
cout << "text_file size " << text_file.size() << endl;
// create Query object
Query query = Query();
// set the members, bool erl = true
query.setoptions( "", erl, text_file );
// call 2nd method
q2 = query.build_query();
Can't really tell whats going on without the full code, but I suspect that you're returning a query object from query.build_query that isn't a full copy of the query object, if that makes sense? Can you include the full text of build_query?
Also, I'd make the build_query method void, and not try to assign a fresh Query object back to a second Query object (q2) at all (unless you really need to, again, can't really tell without the full code), something like this:
void Query::build_query( ) {
std::cout << "kids size" << m_kids.size() << std::endl;
std::cout << "m_qtext_where " << m_qtext_where << std::endl;
}
main
{
...
Query query = Query();
// set the members, bool erl = true
query.setoptions( "", true, text_file );
// call 2nd method
query.build_query();
}
Also, just being pedantic here, but given that you're providing default args for all the options, I'd be inclined to initialise them in the constructor like this:
Query::Query()
: m_qtext_where("")
, qtext_erl(true)
, kids (std::vector<std::string>()
{}
And then instead of a setOptions method, have setters for each individual variable:
void setWhere(std::string qtext_where) {m_qtext_where = qtext_where ;}
void setErl(bool query_erl) { m_erl = query_erl; }
void setKids(std::vector<std::string> kids) { m_kids = kids; }
which you call only when you need to..

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