I have a recursive function find() that tries to find a item with an given ID. Below I extracted the relevant parts from the class to make an example to compile:
#include <iostream>
#include <cstdarg>
#include <cstdio>
#include <string>
#include <vector>
class Item {
private:
std::vector<Item> subitems;
public:
std::wstring id;
public:
Item()
: subitems(0), id(L"") {}
Item(const Item& rhs)
: subitems(rhs.subitems.size()) {
for (std::size_t i = 0; i < rhs.subitems.size(); ++i)
subitems[i] = rhs.subitems[i];
id = rhs.id;
}
Item& operator==(const Item& rhs) {
if (this != &rhs) {
for (std::size_t i = 0; i < rhs.subitems.size(); ++i)
subitems[i] = rhs.subitems[i];
id = rhs.id;
}
return *this;
}
std::vector<Item> getSubitems() {
return subitems;
}
Item addSubitems(Item * item ...) {
va_list args;
va_start(args, item);
for (Item * arg = item; arg != NULL; arg = va_arg(args, Item *)) {
subitems.push_back(*item);
}
va_end(args);
return *this;
}
Item addSubitems(std::vector<Item>& items) {
for (typename std::vector<Item>::value_type &item : items) {
subitems.push_back(item);
}
return *this;
}
static Item * find(int id, std::vector<Item>& items) {
std::wstring id_str = std::to_wstring(id);
std::wcout << "--> find id=" << id_str << std::endl;
std::wcout << "size of items=" << items.size() << std::endl;
for (typename std::vector<Item>::value_type &c : items) {
std::wcout << "it .. cur id=" << c.id << std::endl;
if (!c.id.empty() && c.id == id_str) {
std::wcout << "==> found" << std::endl;
return &c;
}
if (!(c.getSubitems()).empty()) {
std::wcout << "-> find " << id << " in subitems" << std::endl;
std::vector<Item> subcls = c.getSubitems();
std::wcout << "size of subitems=" << subcls.size() << std::endl;
Item * sub = find(id, subcls);
if (sub != NULL) {
std::wcout << "==> found in subitems" << std::endl;
return sub;
}
}
}
return NULL;
}
};
int main() {
Item c1;
c1.id = L"0";
Item c2;
c2.id = L"1";
Item c3;
c3.id = L"2";
Item c4;
c4.id = L"3";
//std::vector<Item> cll4({c4});
//std::vector<Item> cll3({c3});
//std::vector<Item> cll2({c2});
c3.addSubitems(&c4, NULL);
c2.addSubitems(&c3, NULL);
c1.addSubitems(&c2, NULL);
//c1.addSubitems(cll2);
//c2.addSubitems(cll3);
//c3.addSubitems(cll4);
std::vector<Item> items({c1});
Item * c = Item::find(2, items);
std::wcout
<< "Found item="
<< ((c != NULL && c == &c3) ? "true" : "false") << std::endl;
std::wcout
<< ((c != NULL) ? c->id : L"") << std::endl;
return 0;
}
I create a few Items and add sub-Items to them. Now I want to be able to lookup an ID of an item and return the found item or sub-item object by using the recursive find() method.
If I add items with the addSubitems() (with variable args), it will find the item but doesn't return a (valid) item object. If I use the addSubitems method by passing a vector of items the find() method does not recurse fully all subitems.
Actually I am sitting on this problem now the last 4 hours and I am out of ideas, it might be a simple thing I oversee or miss. I added the copy constructor / and assignment operator afterwords (just to see if there are changes in behaviour), but no. Do not worry about the item ID being a string type (the reason is for later serialization), this class is in its early stages so I for now I chose a string type.
Could somebody please point me the flaws/problems to get this class straight! Thanks so much in advance!
Well, one problem with the "it will find the item but doesn't return a (valid) item object." is:
You send Item* to the addSubItems method and then you add a (*Item) to the vector;
This will initialize a copy c'tor, so later on, when you do is &c == &c3, obviously it would be false, since while the objects are INDEED identical, the addresses would not be since they are copies of one another.
Not that I understand why you would want to copy but the solution would be to either test
if (c == c3) -> activating the Item operator ==
OR allocate the members, save a
std::vector<Item*> subitems;
and then ask if (c == c3) -> asking about the addresses
Related
For example: I have class "ListNode" which is linked list member, and i overloaded a [] operator for List class itself, but every time i need to get data from a node, i need to write list[0]->getData() or if I want to set new data, I need to write list[0]->setData(*somedata*).
But I want to make it be able to work like for example like list[0] = 5, so first Node's data is setted to 6. Same with functions. What should I do if I have Node which has int data, and I need to transfer it's data to a function, but without writing Node->getData() all the time?
You can make your operator[] return a reference to the element you want to access.
The example below is a simplified version of what would be a proper solution, including a const version for the operator[].
[Demo]
#include <iostream> // cout
#include <stdexcept> // range_error
struct Node
{
int value{};
Node* next{};
int& operator[](size_t pos)
{
if (pos == 0) { return this->value; }
else if (next == nullptr) { throw std::range_error{"out of bounds"}; }
else { return (*next)[pos - 1]; }
}
};
int main()
{
Node n5{5, nullptr};
Node n4{4, &n5};
Node n3{3, &n4};
Node n2{2, &n3};
Node list{1, &n2};
try
{
std::cout << "list[3]: " << list[3] << "\n";
list[3] = 25;
std::cout << "list[3]: " << list[3] << "\n";
std::cout << "list[13]: " << list[13] << "\n";
}
catch (const std::exception& exc)
{
std::cout << "Error: " << exc.what() << ".\n";
}
}
I have the following code for add a structure to an unordered_set. Now i want to search if a name of a student is already in the unordered_set. How do i do this? When i create a key i need to pass three parameters but i want to search just for the first one, the name. If i create a key just for the first parameter i got an error.
#include <iostream>
#include <unordered_set>
using namespace std;
struct Person {
string name, biology;
int scoreBio;
//param constructor
Person(string pName, string pBiology, int pscoreBio)
{
name = pName;
biology = pBiology;
scoreBio = pscoreBio;
}
bool operator==(const Person& h) const
{
return name == h.name && biology == h.biology && scoreBio == h.scoreBio;
}
};
class MyHashFunction {
public:
// We use predfined hash functions of strings
// and define our hash function as XOR of the
// hash values.
size_t operator()(const Person& h) const
{
return (hash<string>()(h.name)) ^ (hash<string>()(h.biology)) ^ (hash<int>()(h.scoreBio));
}
};
int main()
{
unordered_set<Person, MyHashFunction> Student;
Person p1("Mauro", "Biology", 56);
Person p2("Ram", "Singh", 67);
Person p3("kartik", "kapoor", 56);
Student.insert(p1);
Student.insert(p2);
Student.insert(p3);
Person key("Mauro", " ", 0);
if (Student.find(key) == Student.end())
cout << " not found" << endl << endl;
else
cout << "Found " << endl << endl;
for (auto e : Student) {
cout << e.name << " " << e.biology << " " << e.scoreBio << endl;
}
return 0;
}
The find function in unordered_set searches for keys in the set. It looks for a match against a Person, since that's the type of the key in the map. There are no Persons in the map with a value of { "Mauro", " ", 0 }, so the find call is returning end().
There's no provision in the member find call for searching for part of the key.
You could use the free algorithm find with a custom predicate here:
std::find(Student.begin(), Student.end(),
[] (const Person &p) { return p.name == "Mauro"; });
but that will perform a linear search of the collection, not a hash-based lookup.
I want to write a parameter server in C++ where I can recursively dump a tree of parameters into a property tree and then write it to a JSON file.
The dump function looks like this:
void Params::dump(string filename) {
// Create a root
pt::ptree root;
// Fill the root with parameters
mapToPt(curParams, root);
// Write to cout
pt::write_json(cout, root);
}
mapToPt is supposed to recursively go through the hierarchy of my parameter server and fill the property tree while doing so:
void Params::mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root) {
// Fill current root with parameters from curParams ParameterMap
map<string, boost::shared_ptr<Param> >::iterator it;
for ( it = curParams->getParamMap().begin(); it != curParams-getParamMap().end(); it++ ) {
root.put(it->first, it->second->getValue());
cout << "Add Parameter: \n";
cout << "Parameter name: " << it->first << "\n";
cout << "Parameter value: " << it->second->getValue() << "\n";
}
// Recursively go through all children to do the same to them
if(curParams->hasChildren()) { //ERROR LINE
map<string, boost::shared_ptr<Params> >::iterator it;
for ( it = curParams->getChildren().begin(); it != curParams-getChildren().end(); it++ ) {
pt::ptree new_tree;
root.add_child(it->second->getName(), new_tree);
cout << "Add Child: \n";
cout << "Child name: " << it->second->getName() << "\n";
mapToPt(it->second, new_tree);
}
}
}
My problem is that as soon as I go into recursion, errors occur at random lines that can not be the cause of the error. "basic_string::_M_construct null not valid" is the error message. I believe that I might access deleted content and that it might be due to the way I iterate through the property tree children.
Is my way of doing that wrong or is there another way to do it?
Thank you.
Why is mapToPt a member when it also expects a pointer to a Params instance?
Anyhoops, there's quite a bit of confusion.
At a design level, your Params type looks like it cannot decide whether it's a leaf node or not. Moreover, it suffers from "Quasi Classes" design, where getters essentially guarantee that there is no class invariant possible. In such cases, prefer to just have a struct with member fields.
Note, if you fail to return by reference from getParamMap() and getChildren() then you already have Undefined Behaviour in both loops, because the iterators then point into non-existent copies of containers.
You should check this. Also, see my working demo below
At the implementation level, this is causing you problems:
pt::ptree new_tree;
root.add_child(it->second->getName(), new_tree);
add_child inserts a copy of new_tree. Any future modification to new_tree has no effect. Instead, write:
pt::ptree& new_tree = root.add_child(it->second->getName(), {});
Here, new_tree becomes a reference to the actually added tree.
Attempted Fix
The style is still below my expectations. Personally I'd closely review the use of shared_ptr at all in this piece of code.
But this will probably help you along:
Live On Coliru
#include <boost/make_shared.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
#include <map>
namespace pt = boost::property_tree;
struct Param {
std::string getValue() const { return "42"; }
};
struct Params {
using ParamMap = std::map<std::string, boost::shared_ptr<Param> >;
using Children = std::map<std::string, boost::shared_ptr<Params> >;
Params(std::string name = "") : _name(name) {}
std::string getName() const { return _name; }
ParamMap& getParamMap() { return _map; }
ParamMap const& getParamMap() const { return _map; }
bool hasChildren() const { return !_children.empty(); }
Children& getChildren() { return _children; }
Children const& getChildren() const { return _children; }
static void mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root);
private:
std::string _name;
ParamMap _map;
Children _children;
};
void Params::mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root) {
// Fill current root with parameters from curParams ParameterMap
std::map<std::string, boost::shared_ptr<Param> >::iterator it;
for (it = curParams->getParamMap().begin(); it != curParams->getParamMap().end(); it++) {
root.put(it->first, it->second->getValue());
//std::cout << "Add Parameter: \n";
//std::cout << "Parameter name: " << it->first << "\n";
//std::cout << "Parameter value: " << it->second->getValue() << "\n";
}
// Recursively go through all children to do the same to them
if (curParams->hasChildren()) {
for (auto it = curParams->getChildren().begin(); it != curParams->getChildren().end(); it++) {
pt::ptree& new_tree = root.add_child(it->second->getName(), {});
//std::cout << "Add Child: \n";
//std::cout << "Child name: " << it->second->getName() << "\n";
mapToPt(it->second, new_tree);
}
}
}
int main() {
auto a = boost::make_shared<Params>("rootparams");
a->getParamMap().emplace("one", boost::make_shared<Param>());
a->getParamMap().emplace("two", boost::make_shared<Param>());
a->getParamMap().emplace("three", boost::make_shared<Param>());
a->getChildren().emplace("child1", boost::make_shared<Params>("child1-name"))
.first->second->getParamMap().emplace("four", boost::make_shared<Param>());
a->getChildren().emplace("child2", boost::make_shared<Params>("child2-name"))
.first->second->getParamMap().emplace("five", boost::make_shared<Param>());
pt::ptree root;
a->mapToPt(a, root);
write_json(std::cout, root);
}
Prints
{
"one": "42",
"three": "42",
"two": "42",
"child1-name": {
"four": "42"
},
"child2-name": {
"five": "42"
}
}
I am playing with c++ code today. Learning about std containers. I'm trying to insert and update data in a std::map but for some reason I can't insert values into a map. Keys will insert but not values. The code at the bottom will print the following if you enter something into the terminal that opens. In this example I entered "test". Anyway, my questions are, why is the insert returning false, why in the value not inserting?
test
first
failed
Context1 :
Here is the code:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <map>
#include <random>
static std::map<std::string, std::string> currentFullState;
static const std::string sDEFAULT_STRING = "";
void PringCurrentState()
{
std::map<std::string, std::string>::iterator stateData = currentFullState.begin();
while (stateData != currentFullState.end())
{
std::cout << stateData->first << " : ";
std::cout << stateData->second << std::endl;
stateData++;
};
}
void UpdateState(std::string context, std::string data)
{
if (currentFullState[context] == sDEFAULT_STRING)
{
// first entry, possibly special?
std::cout << "first" << std::endl;
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
if (result.second == false)
std::cout << "failed" << std::endl;
else
std::cout << "good" << std::endl;
}
else if (data != currentFullState[context])
{
// change in value
}
else
{
currentFullState[context] == data;
}
}
void DoWork()
{
if (rand() % 2)
{
UpdateState("Context1", "Data1");
}
else
{
UpdateState("Context2", "Data2");
}
}
int main()
{
std::string command = "";
for (;;)
{
PringCurrentState();
std::cin >> command;
DoWork();
if (command == "q")
{
break;
}
}
return 0;
}
Why does the insert not work?
Certainly would help if you wrote
currentFullState[context] = data;
instead of
currentFullState[context] == data;
Also
auto result = currentFullState.insert(std::make_pair(context, data));
should be preferred to
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
Slightly surprised that the second one compiles.
=========================================================================
The real reason the insert fails is that you are adding that key for the second time. This is the first time
if (currentFullState[context] == sDEFAULT_STRING)
operator[] on a map always adds the key to the map. This is why your second attempt to add with
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
fails, the key is already present. If you had written
currentFullState[context] = data;
Then it would work.
I am trying to understand a code, here is fragment which is causing confusion:
typedef map<int, Person, less<int> > people_map;
people_map people;
.
.
.
cout << "Erasing people of age 100" << endl;
for (people_map::iterator j = people.begin(); j != people.end();) {
if (j->second.GetAge() == 100)
{
people.erase(j++); // iterator is advanced before the erase occurs
}
else
++j; // advance the iterator
} // end of erase loop
the confusion is: if i want to increment j after the function call it causes segmentation fault. I am not able to understand why:
I change it to something like this:
if (j->second.GetAge() == 100)
{
temp = j++;
j--;
people.erase(j); // iterator is advanced before the erase occurs
j=temp;
}
causes segmentation fault.
or like this:
if (j->second.GetAge() == 100)
{
people.erase(j); // iterator is advanced before the erase occurs
j++;
}
causes segmentation fault.
here is the complete program listing:
// disable warnings about long names
#ifdef WIN32
#pragma warning( disable : 4786)
#endif
#include <string>
#include <map>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;
class Person {
// private members
string m_sName;
string m_sEmail;
int m_iAge;
public:
// constructor
Person(const string sName,
const string sEmail,
const int iAge) :
m_sName(sName), m_sEmail(sEmail), m_iAge(iAge) {
};
// default constructor
Person() : m_iAge(0) {
};
// copy constructor
Person(const Person & p) :
m_sName(p.m_sName), m_sEmail(p.m_sEmail), m_iAge(p.m_iAge) {
};
// operator =
Person & operator=(const Person & rhs) {
// don't assign to self
if (this == &rhs)
return *this;
m_sName = rhs.m_sName;
m_sEmail = rhs.m_sEmail;
m_iAge = rhs.m_iAge;
return *this;
};
// access private members
string GetName() const {
return m_sName;
};
string GetEmail() const {
return m_sEmail;
};
int GetAge() const {
return m_iAge;
};
}; // end of class Person
// function object to print one person
class fPrint {
ostream & m_os;
public:
// constructor - remember which stream to use
fPrint(ostream & os) : m_os(os) {
};
// person object arrives as a pair of key,object
void operator() (const pair <const int, const Person> & item) const {
m_os << "# " << item.first << " - name: "
<< item.second.GetName()
<< " - " << item.second.GetEmail()
<< ", age " << item.second.GetAge()
<< endl;
};
}; // end of class fPrint
// declare type for storing people (numeric key, person object)
typedef map<int, Person, less<int> > people_map;
int main(void) {
// make a map of people
people_map people;
// add items to list
people [1234] = Person("Nick", "nick#some-email-address.com", 15);
people [4422] = Person("Fred", "fred#nurk.com.au", 100);
people [88] = Person("John", "john#smith.com.au", 35);
// insert a different way ...
people.insert(make_pair(42, Person("Abigail", "abigail#blah.com.au", 22)));
// best to declare this on its own line :)
fPrint fo(cout); // instance of function output object
// print everyone (calls a function object to print)
cout << "Printing all using fPrint ..." << endl;
for_each(people.begin(), people.end(), fo);
// find someone by key
cout << "Finding person 4422 ..." << endl;
people_map::const_iterator i = people.find(4422);
if (i == people.end())
cout << "Not found." << endl;
else {
fo(*i); // dereference and print
// another way of printing -
// key itself is the "first" part of the map pair ...
cout << "Found key = " << i->first << endl;
// person object is the "second" part of the map pair...
cout << "Found name = " << i->second.GetName() << endl;
}
// Note, this will not work:
// fPrint (cout) (*i);
// However this will:
// 0, fPrint (cout) (*i);
// However I think the extra zero is a bit obscure. :)
// An alternative way of finding someone.
// Note - this will add them if they are not there.
// Since this is a reference changing it will change the person in the
// map. Leave off the & to get a copy of the person.
Person & p = people [1234];
cout << "Person 1234 has name " << p.GetName() << endl;
// Example of erasing an element correctly ...
// If we did the j++ as part of the for loop we would end up
// adding 1 to an iterator that pointed to an element that was
// removed which would lead to a crash. See Josuttis p 205.
cout << "Erasing people of age 100" << endl;
for (people_map::iterator j = people.begin(); j != people.end();) {
if (j->second.GetAge() == 100)
{
people.erase(j++); // iterator is advanced before the erase occurs
}
else
++j; // advance the iterator
} // end of erase loop
// now display who is left
cout << "Printing people left after erase ..." << endl;
for_each(people.begin(), people.end(), fo);
return 0;
} // end of main
erase invalidates the iterator to the erased element.
if (j->second.GetAge() == 100)
{
temp = j++;
j--;
people.erase(j); // iterator is advanced before the erase occurs
j=temp;
}
This doesn't work because you set temp equal to the old value of j, and hence you'll keep using the invalidated iterator. The result of post-increment is the original value of the operand.
I suppose you could also do it like this, which is functionally the same as the working code, except it doesn't use the temporary result of post-increment:
if (j->second.GetAge() == 100) {
temp = j;
++j;
people.erase(temp);
}
Erase invalidates your iterator, this is why postfix increment is used. It doesn't pass advanced iterator to erase, it passes current iterator but advances as a sideffect.
people_map::iterator erase_it = j++;
people.erase(erase_it);
You have a problem with postfix increment, this is why your attempts have failed.
int i = 1;
int j = i++; // j == 1, i == 2