using Boost `ptree.find` doesn't work as expected - c++

I have
const boost::property_tree::ptree& v
and I want to get <xmlattr>.Value, if it exists, otherwise the value.
I tried this code:
if(v.find("<xmlattr>.Value") != v.not_found())
value = v.get<std::string>("<xmlattr>.Value");
else
value = v.get_value<std::string>();
However, it doesn't work as expected. Even if the value is there, find() returns not_found().
This code works:
auto inValue = v.get_optional<std::string>("<xmlattr>.Value");
if(inValue.is_initialized())
value = inValue.get();
else
value = v.get_value<std::string>();
I guess I understood find() wrong. What exactly does it do? Is there another function I should use instead?

According to the documentation, find() (see here) finds a child with the given key (not path), or not_found() if there is none.
<xmlattr>.Value is a path (that works with get and get_optional).

Related

std::unordered_map::insert vs std::unordered_map::operator[]

I have a container of type unordered_map and I was wanting confirmation of which version I should use if I want to add an element to the map. I want it to overwrite the old value with the new presented if it exists and just add it if it does not.
I see that insert adds the element if it exits and also returns a pair of iterator and bool where the bool indicates if the insert is successful. I also see that operator[] adds the element if it does not exist and overwrites it if it does.
My question is basically if I should I be using operator[] for this purpose or are there any gotchas that I haven't considered. Also if my perception of these methods is wrong, please correct me.
here is what I was going to do. Data is a scoped enum of storage type int
void insertData(const Data _Data, const int _value)
{
int SC_val = static_cast<int>(_Data);
//sc val is now the integer value of the Data being added
//returns a pair of iterator and bool indicating whether the insert was successful
auto ret = baseData.insert(std::pair<int,int>(SC_val,_value));
if (ret.second == false)
{//if the insert was not successful(key already exists)
baseData[ret.first->first] = _value;
}
}
or should I just do
int index = static_cast<int>(_Data);
baseData[index] = _value;
I am leaning towards the operator[] version as I see no real difference and it is much less code. Please advise and thank you all in advance.
insert and operator[] are both very useful methods. They appear similar, however, the details make them very different.
operator[]
Returns a reference to the element you are searching for. When no element exists, it creates a new default element. (So requires default constructor)
When used to insert an element: myMap[key] = value;, the value will override the old value for the key.
insert
Returns an iterator and a bool. The iterator is to the element. The bool indicates if a new element was inserted (true), or it already contained an element for the key (false).
Using insert doesn't require a default constructor.
When used to insert a new element: myMap.insert({key, value});, the old value does not get updated if key already exists in the map.
insert_or_assign
Tnx to Marc Glisse who mentioned it in the comments.
This method is similar to insert. The difference is in the behavior when the element already exists, in which case it will override the existing element.
Returns an iterator and a bool. The iterator is to the element. The bool indicates if a new element was inserted (true), or it already contained an element for the key (false).
Using insert_or_assign doesn't require a default constructor.
When used to insert a new element: myMap.insert({key, value});, the old value gets updated if key already exists in the map.
Building your map
Your use-case inserts data into the map and assumes that the key doesn't exist.
Writing baseData[index] = _value; will exactly do what you want.
However, if I would have to write it, I would go with the insert variant:
auto successfulInsert = baseData.emplace(SC_val, _value).second;
assert(successfulInsert && "Value has been inserted several times.");
Just using operator [] perfectly fits for your case.
FYI: Quote from cppreference.com std::unordered_map:
std::unordered_map::operator[]
Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.
I see no real difference and it is much less code.
You're right!
It seems that you want to insert data only when it is not exist in the baseData.
You can use count() to check if the data is in the map like this:
int index = static_cast<int>(_Data);
if(!baseData.count(index))
{
baseData[index] = _value
}

How to search by member accessor value with std::find_if()?

I am learning C++ at the moment and have an example program implemented with an array of objects data store. To make some other operations easier, I have changed the store to a vector. With this change I am now not sure of the best way to search the store to find an object based on a member accessor value.
Initially I used a simple loop:
vector<Composer> composers; // where Composer has a member function get_last_name() that returns a string
Composer& Database::get_composer(string last_name)
{
for (Composer& c : composers)
if (c.get_last_name().compare(last_name))
return c;
throw std::out_of_range("Composer not found");
}
This works just fine of course, but to experiment I wanted to see if there were vector specific functions that could also do the job. So far I have settled on trying to use find_if() (if there is a better function, please suggest).
However, I am not sure exactly the correct way to use find_if(). Based on code seen in online research I have replaced the above with the following:
vector<Composer> composers; // where Composer has a member function get_last_name() that returns a string
Composer& Database::get_composer(string last_name)
{
auto found = find_if(composers.begin(), composers.end(),
[last_name](Composer& c) -> bool {c.get_last_name().compare(last_name);});
if (found == composers.end())
throw out_of_range("Composer not found");
else
return *found;
}
This does not work. It does find a result, but it is the incorrect one. If an argument that matches, say the third composer's last name the function always returns the first item from the vector (if I pass an argument that doesn't match any last name the function correctly throws an exception)... what am I doing wrong?
You are on the right track, your lambda needs return statement. Also in such case you do not have to specify it's return type explicitly, it can be deduced:
find_if(composers.begin(), composers.end(),
[last_name](const Composer& c) { return c.get_last_name() == last_name);});
you original code should not compile or at least emit warning(s), you should pay attention to them.
Note: it is not clear how your original code worked if you tested it, it should be:
if (c.get_last_name().compare(last_name) == 0 )
or simply:
if (c.get_last_name() == last_name )
as std::string::compare() returns int -1 0 or 1, so your code searches for string that does not match variable last_name
With range-v3, you may use projection:
auto it = ranges::find(composers, last_name, &composers::get_last_name);

How to retrieve value from a map in c++

I have a map with some values. Then I have a function that returns some string, and this string will be a member of the keys in map. I need to retrieve the value based on the key and pass it to another function, which takes it as argument.
map<string,int> SymbolTable;
SymbolTable["R0"]=0;
SymbolTable["R1"]=1;
SymbolTable["R2"]=2;
SymbolTable["R3"]=3;
string value=getValue(); //this one will return something from R0 to R3
nextFunction(SymbolTable[value]); // this part is wrong
If I give value=="R0" or some static value, this is working as expected. But whenever I pass this dynamic value, it returns 0 always, so my nextFunction is taking 0 as argument.
I tried to output the return value from getValue() to check what it is returning, and it is correct. I have tried this and similar ways, but all gives me the same issue. Can someone guide me on what am I doing wrong here? TIY
If I give value=="R0" or some static value, this is working as
expected. But whenever I pass this dynamic value, it returns 0 always
It simply means the "dynamic value" you obtained does not exist as a key in the map. std::map's operator [] inserts a default constructed value if the associated key does not exist.
To check for the existence of value in your map, you can do:
string value=getValue();
if(SymbolTable.count(value)){
//key exists....
nextFunction(SymbolTable[value]); // this part should be correct now
}
or you can equally use std::map::find

map: Bad Ptr even if the key was found

I have a map inside struct:
struct amountOfDist
{
int time;
vector<int> distVector;
map<string,int> pairsMap;
};
amountOfDist m_tempDistStruct;
when the code runs, it crushes when I try to find a value:
if(m_tempDistStruct.pairsMap.find("(1,2)")->second != 1)
{
...
}
I tried to isolate the command by:
map<string,int>::iterator it;
it = m_tempDistStruct.pairsMap.find("(1,2)");
and I get padptr. But when I put break point on the line:
it = m_tempDistStruct.pairsMap.find("(1,2)");
I can see that the map hols all the keys and values (the correct ones) and the key (1,2) exists.
Why does the find command returns badptr?
I will be happy for guidance.
Thanks.
If the map really does contain the key you're looking for, then your code should work; something else has gone horribly wrong if it doesn't. However, the code is rather fragile, since it will give undefined behaviour if the key is missing.
You have to check whether find succeeded before dereferencing it; if it fails, then it returns a past-the-end iterator which isn't dereferencable. Alternatively, use [] which will insert a new element if it was missing.
So safer versions are:
// use find and check it exists
auto found = map.find(key);
if (found != map.end() && found->second != 1)
// use [] to insert if it doesn't exist
if (map[key] != 1)
If neither of these work, or you're absolutely sure that the key must exist, then we'll need to see a complete test case to figure out what's going wrong with the code you haven't shown us.
Using your code, I get totally correct behavior. So I am pretty sure that the value you're looking for is not in the map.
amountOfDist m_tempDistStruct;
m_tempDistStruct.pairsMap["(1,2)"] = 10;
if (m_tempDistStruct.pairsMap.find("(1,2)")->second == 10)
{
cout << "GOT HERE!" << endl;
}
The above code snippet adds "(1,2)" => 10 to the map, then finds it just fine. I suspect the key in your map is subtly different to what you think it is.

Can I get a value from std::map without crashing if the key doesn't exist?

I have C++ code like this:
if(rtstructure.find(varName) != rtstructure.end()) {
rtdef = rtstructure[varName];
}
where rtstructure is std::map with std::string for the key.
This code works but it seems like a waste to make it search twice for the same key. If I omit the if case around the assignment, the program crashes if varName points to a key that doesn't exist.
Can I in a single map operation look up a key in a std::map and get its value if it exists, without crashing if it doesn't exist?
find give you a std::map<>::iterator that holds/point to std::pair<>. The iterator can be saved and reused (given that you did not do anything to invalidate it such as erase).
// i don't know the type of rtstructure so i use auto
// you can replace it to the correct type if C++11 is not available
auto it = rtstructure.find(varName);
if(it != rtstructure.end()) {
rtdef = it->second;
}