I have a question about changing or replacing elements in a list. I have a class:
class Node{
public:
elType type; // enum
string name;
Node(elType type, string name);
~Node();
void printNodeInfo();
}
and a list:
std::list <Node * > someList;
Now how can i replace a value (for eaxmple change type) in such element.
I've tried this:
std::list<Node * >::iterator it = someList.end();
it--;
while(openName.compare((*it)->name) != 0)
it--;
(*it)->type = otherType;
But it does not seem to work, the type stays the same. I'd be grateful for any help.
EDIT:
I updated the list so now I have:
std::list <Node> someList;
and changed the replacing to:
it->type = otherType;
I also tried:
std::list<Node >::iterator it2 = someList.erase(it);
Node temp(otherType, openName);
someList.insert(it2, temp);
after this for both cases a simple method for printing :
it2->printNodeInfo();
outputs nothing.
I don't know what is your problem exactly, but here is your solution:
#include <iostream>
#include <string>
#include <list>
using namespace std;
class Node{
public:
int type; // enum
string name;
Node(int type, string name) : type(type), name(name) {}
~Node(){}
void printNodeInfo() const {cout << type << ", " << name << endl;}
};
void replace(list<Node> &l, const string &nameSearch, int typeReplace) {
for (auto it = l.rbegin(); it != l.rend(); it++) {
if (it->name == nameSearch) {
it->type = typeReplace;
/* To stop searching */
return;
}
}
/* Nothing replaced, error message? */
}
int main() {
list<Node> l;
l.push_back(Node(0, "World"));
l.push_back(Node(1,"Hello"));
l.push_back(Node(2,"World"));
replace(l, "World", 42);
for (const auto &el: l) {
el.printNodeInfo();
}
return 0;
}
I wonder why you use Node * instead of Node. Consider using list<Node> instead of list<Node *> unless you can give a reason why not.
I assume a list<Node> for now.
To find an element use find from the STL. You can use it like that find(someList.begin(), someList.end(), value); value would be of type elType in your case.
You also have to provide a comparison operator that compares a node's name to a give name.
Not having enough information I made a simplified example of yours. Maybe that gets you corrected/closer to what you want to achieve.
// Example program
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
enum elType { red, green, blue };
using namespace std;
class Node{
public:
elType type; // enum
string name;
Node(elType type, string name);
};
Node::Node(elType type, string name) {
this->type = type;
this->name = name;
};
// ensure that name can be found using find of STL
bool operator==(Node &n, string name) {
return n.name == name;
}
int main() {
// Create nodes
Node n1(elType::red, "Node 1");
Node n2(elType::green, "Node 2");
Node n3(elType::blue, "Node 3");
// Output node names and types
cout << n1.name << " : " << n1.type << endl;
cout << n2.name << " : " << n2.type << endl;
cout << n3.name << " : " << n3.type << endl;
// Create list of nodes
list<Node> someList{ n1, n2, n3 };
// find node with name "Node 3"
auto it = find(someList.begin(), someList.end(), "Node 3");
// if a node was found change its type to red
if ( it != someList.end() ) {
it->type = elType::red;
}
// output nodes in list
cout << endl;
for ( auto node: someList ) {
cout << node.name << " : " << node.type << endl;
}
return 0;
}
You can, as mentioned by other users, also use the reverse iterator.
In that case simple replace begin() and end() by rbegin() and rend() like so:
// find node with type "blue" and change type to "red"
auto it = find(someList.begin(), someList.end(), "Node 3");
// if a node was found change its type
if ( it != someList.end() ) {
it->type = elType::red;
}
Related
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 trying out simple class and linked list implementation.
Please help me out with this code as i am receiving
"list iterator not dereferencable"
when running the code.
Thanks
#include<iostream>
#include<list>
#include<string>
using namespace std;
class Car
{
public:
void getType(string x)
{
type = x;
}
string showType()
{
return type;
}
private:
string type;
};
void main()
{
string p;
list<Car> c;
list<Car>::iterator curr = c.begin();
cout << "Please key in anything you want: ";
getline(cin, p);
curr->getType(p);
cout << "The phrase you have typed is: " << curr->showType() << endl;
}
Write the following way
cout << "Please key in anything you want: ";
getline(cin, p);
c.push_back( Car() );
list<Car>::iterator curr = c.begin();
curr->getType(p);
And it is much better to rename the member function getType to setType.:)
Take into account that the function main without parameters shall be declared like
int main()
^^^
You didn't insert anything into your list. So the iterator is not valid, it's pointing to c.end() and dereferencing it is undefined behaviour.
Instead add a Car to your list before you get the begin iterator.
#include<iostream>
#include<list>
#include<string>
using namespace std;
class Car
{
public:
void setType(const string& x)
{
type = x;
}
string showType()
{
return type;
}
private:
string type;
};
int main()
{
string p;
list<Car> c;
c.push_back(Car{});
auto curr = c.begin();
cout << "Please key in anything you want: ";
getline(cin, p);
curr->setType(p);
cout << "The phrase you have typed is: " << curr->showType() << endl;
}
When I try to compile the code below I get an error:
src/main.cpp:51:48: error: no matching member function for call to 'child_value'
std::cout << "which has value" << eb.second.child_value(kv.second);
~~~~~~~~~~^~~~~~~~~~~
What I don't understand is I was able to use this in the loop above. I can only assume it wants me to use kv.second.child_value(kv.second); instead. However I want it to run this code on the xml returned by for (auto& eb: mapb) {.
#include "pugi/pugixml.hpp"
#include <iostream>
#include <string>
#include <map>
int main()
{
const std::map<std::string, std::string> tagMap {
{"description", "content"}, {"url", "web_address"}
};
pugi::xml_document doca, docb;
std::map<std::string, pugi::xml_node> mapa, mapb;
if (!doca.load_file("a.xml") || !docb.load_file("b.xml")) {
std::cout << "Can't find input files";
return 1;
}
for (auto& node: doca.child("data").children("entry")) {
const char* id = node.child_value("id");
mapa[id] = node;
}
for (auto& node: docb.child("data").children("entry")) {
const char* idcs = node.child_value("id");
if (!mapa.erase(idcs)) {
mapb[idcs] = node;
}
}
// For removed
for (auto& ea: mapa) {
std::cout << "Removed:" << std::endl;
ea.second.print(std::cout);
}
// For added nodes
for (auto& eb: mapb) {
// Loop through tag map
for (auto& kv : tagMap) {
// Try to find the tag name named in second map value
// and associate it to the type of information in first map value
std::cout << "Found" << kv.first;
std::cout << "which has value" << eb.second.child_value(kv.second);
}
}
}
If anyone could explain what I am doing wrong I would really appreciated it.
According to the two overloads found here
// Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
const char_t* child_value() const;
// Get child value of child with specified name. Equivalent to child(name).child_value().
const char_t* child_value(const char_t* name) const;
you need to pass a pointer to a string (or string literal).
std::cout << "which has value" << eb.second.child_value(kv.second.c_str());
^^^^^^^^^^^^^^^^^
My c++ is really poor. Anyhow with the code snippet bellow why do I get a error on the << in the do while loop when outside of it I get no error. The error is: no operator "<<" matches these operands. However the string w picks up the word fine. I read somewhere I may have to overload it but why? And how would I over load it for a link list.
Thanks in advance.
void print()
{
HashTable *marker = headOne;
HashTable *inList;
for( int i = 0; i < tableSize; i++ )
{
cout << i << ": " << marker->number << endl;
if(marker->child != NULL)
{
inList = marker;
do
{
string w = inList->word;
cout << w << endl;
inList = inList->child;
}
while(inList != NULL);
}
marker = marker->next;
}//end for loop
}
In order to be able to cout a std::string you have to include:
#include <string>
#include <iostream>
This works:
// Missing includes and using
#include <string>
#include <iostream>
using namespace std;
// missing struct
struct HashTable {
HashTable* next;
HashTable* child;
string word;
int number;
};
// missing vars
HashTable ht;
HashTable* headOne = &ht;
int tableSize = 5;
// Unchanged
void print()
{
HashTable *marker = headOne;
HashTable *inList;
for( int i = 0; i < tableSize; i++ )
{
cout << i << ": " << marker -> number << endl;
if(marker->child != NULL)
{
inList = marker;
do
{
string w = inList -> word;
cout << w << endl;
inList = inList -> child;
}
while(inList != NULL);
}
marker = marker -> next;
}//end for loop
}
I read somewhere I may have to overload it but why?
Because there's no overload that matches your need.
And how would I over load it for a link list.
You can do this outside of your class or struct:
(where T is the type of the object that you want to print)
std::ostream& operator<<(std::ostream& os, const T& obj)
{
/* write obj to stream */
return os;
}
This is just an example that prints a vector:
std::ostream& operator<<(std::ostream& os, vector<int>& obj)
{
for (auto &i : obj)
os << i << " ";
return os;
}
Then I would be able to simply do this cout << n.vec; where n is the class object and vec the name of a vector of ints.
I have been working on this project for several days. This project contains 3 classes. The first is a DNA class that stores a DNA object. The second is a database class that reads a file and parses commands and data and deals with it accordingly. The last is a DNA list class that is a linked list of nodes that have pointers to DNA objects.
I've completed my linked list building method. It is required to be a push_back method that adds nodes at the end of the list. My problem arises when I try to search the list for a certain node. This has to be a method that returns a DNA* if a DNA object with id exists in the list; otherwise it returns NULL.
My plan is to use this method to print and also delete the node. I can't seem to get this method to work. Obviously I'm a little shaky with pointers. It took me several hours to implement my push_back method. Here is my code. Any guidance or help is appreciated.
DNA.h
#ifndef DNA_H
#define DNA_H
#include <string>
class DNA{
public:
// overloaded constructor for DNA class
DNA(std::string, int, std::string, int, int);
// print function
void print();
int getID();
private:
std::string m_label; // variable to hold label
int m_id; // variable to hold id
std::string m_sequence; // variable to hold sequence
int m_length; // variable to hold length
int m_index; // variable to hold index
};
#endif
DNA implementation
#include "DNA.h"
#include <iostream>
#include <string>
using namespace std;
DNA::DNA(string label, int id, string sequence, int length, int index){
m_label = label;
m_id = id;
m_sequence = sequence;
m_length = length;
m_index = index;
}
void DNA::print(){
cout << "DNA:" << '\t' << "label: " << m_label << '\t' << "ID: " << m_id << '\t' << "Sequence: " << m_sequence << endl << "Length: " << m_length << '\t' << "cDNAStartIndex: " << m_index << endl << endl;
}
int DNA::getID(){
return m_id;
}
Database class
#ifndef SEQUENCEDATABASE_H
#define SEQUENCEDATABASE_H
#include <string>
#include <fstream>
#include "DNA.h"
#include "DNAList.h"
class SequenceDatabase {
public:
SequenceDatabase();
// function to import the data file, parse the data, and perform the required output
void importEntries(std::string);
private:
DNAList list;
};
#endif
Database implemenation
#include "SequenceDatabase.h"
#include "DNA.h"
#include "DNAList.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
SequenceDatabase::SequenceDatabase(){
DNAList list;
}
// function reads in the filename creates a data stream and performs the requested actions
void SequenceDatabase::importEntries(string inputFile){
ifstream dnaFile(inputFile);
char command;
string label, sequence;
int id, length, index;
while(dnaFile >> command){
DNA* p;
if(command == 'D'){
dnaFile >> label >> id >> sequence >> length >> index;
DNA data(label, id, sequence, length, index);
p = new DNA(label, id, sequence, length, index);
list.push_back(p);
}
if(command == 'P'){
dnaFile >> id;
cout << "Printing " << id << " ..." << endl << endl;
p = list.findId(id);
if(p == nullptr)
cout << "Can not find item " << "(" << id << ")!" << endl << endl;
else
p-> print();
}
}
dnaFile.close();
}
Finally my list class
#ifndef DNALIST_H
#define DNALIST_H
#include "DNA.h"
#include "sequenceDatabase.h"
struct DNANode{
DNA* data;
DNANode* next;
DNANode* prev;
};
class DNAList{
public:
DNAList();
DNAList(DNA* newDNA);
void push_back(DNA* newDNA);
DNA* findId(int);
void obliterate(int id);
int size();
private:
DNANode* head;
int list_size;
};
#endif
List implementation
#include "DNA.h"
#include "sequenceDatabase.h"
#include "DNAList.h"
#include <iostream>
using namespace std;
DNAList::DNAList(){
head = new DNANode;
head->next = nullptr;
list_size = 0;
}
DNA* DNAList::findId(int id){ // this function is my problem
DNANode* current;
current = head;
while(current->next != nullptr){
if(current->data->getID() == id){
return current->data;
}
current = current->next;
}
return nullptr;
}
int DNAList::size(){
return list_size;
}
void DNAList::push_back(DNA* newDNA){
DNANode* current;
DNANode* last;
DNANode* p;
p = new DNANode;
p->data = newDNA;
last = nullptr;
current = head;
cout << "Adding " << newDNA->getID() << " ..." << endl << endl;
while(current != nullptr){
last = current;
current = current->next;
}
if(current == head->next){
p->next = nullptr;
p->prev = head;
head->next = p;
}
else{
p->next = current;
p->prev = last;
last->next = p;
}
list_size++;
}
I wasn't sure if I should post the whole code, but i felt it was needed to understand the problem. My problem arises when i try to call the find function to print the data in the node.
Your head member variable of DNAList is initialized with new DNANode. Since DNANode doesn't have an explicit constructor, its compiler generated one will not initialize the pointers data, next and prev. next is initialized on the next line, but data is left as a garbage value.
Inside findId, this line is executed:
if (current->data->getID() == id){
However the first time around the loop, current is pointing head. This means you are trying to look at the garbage value, which will possibly crash.
One solution is to change the findId function to start at head->next, another is to initialise the data pointer in head to nullptr and check that data is not nullptr before you access it.
A better solution might be to just have head as nullptr to start with, rather than having a dummy DNANode at the top. This would involve changing some of the code in push_back, but might make it easier to understand.
Aha. I think what is causing the problem is that towards the end of your SequenceDatabase::importEntries() method you are setting if(p=nullptr) instead of making the comparison if(p == nullptr). This is no doubt causing the error you see. This is a common mistake.