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());
^^^^^^^^^^^^^^^^^
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"
}
}
Can someone please give an actual example of function that returns map in c++.
I tried answers from other posts but I don't know how to apply in my case.
This is my working code:
auto DataArray = jvalue.at(U("data")).as_array();
//Make an associative array or map with key value pair from extracted json data
std::map<int, std::string> staffMap;
// loop through 'data' object
for (int i = 0; i < DataArray.size(); i++)
{
try
{
auto data = DataArray[i];
auto dataObj = data.as_object();
int key;
std::string value;
// loop through each object of 'data'
for (auto iterInner = dataObj.cbegin(); iterInner != dataObj.cend(); ++iterInner)
{
auto &propertyName = iterInner->first;
auto &propertyValue = iterInner->second;
//std::wcout << "Property: " << propertyName << ", Value: " << propertyValue << std::endl;
if (propertyName == L"_id")
{
key = propertyValue.as_integer();
}
else if (propertyName == L"name")
{
value = conversions::to_utf8string(propertyValue.as_string());
}
}
staffMap.insert(std::make_pair(key, value));
}
catch (const std::exception& e)
{
std::wcout << e.what() << std::endl;
}
}
// Iterate through map and display in terminal
std::map<int, std::string>::iterator iter;
std::wcout << "The list of staffs" << std::endl;
for (iter = staffMap.begin(); iter != staffMap.end(); iter++)
std::cout << iter->first << " " << iter->second << " ,";
Let say I want a function:
std::map<int, std::string> staffMap;
std::map<> GetStaffMap()
{
return staffMap;
}
// Give staffMap a data here
I can't find enough tutorial for making a function that returns std::map in c++. Hope someone could help me here. Thank you.
I can't find enough tutorial for making a function that returns std::map in c++. Hope someone could help me here
You need to specify the exact type, std::map<int, std::string>:
std::map<int, std::string> GetStaffMap()
{
return staffMap;
}
If you are able to use C++14, use auto as an alternative:
auto GetStaffMap()
{
return staffMap;
}
The example below shows how to create a function in C++ that returns a map.
// Example program with a function returning a map
#include <iostream>
#include <string>
#include <map>
std::map<std::string, int>
function()
{
std::map<std::string, int> out;
out["one"] = 1;
out["two"] = 2;
return out;
}
int main()
{
std::map<std::string, int> out = function();
for (const auto & iter : out)
std::cout << iter.first << " = " << iter.second << std::endl;
}
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;
}
In my C++ program I spit out nodes from an XML file. I have a standard schema which may not be followed by the input file. I therefore need to map a node title with the information type which is contained within it.
#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
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" << node.child_value(kv.second)
}
}
}
The information I am particualy asking for help with is within for (auto& eb: mapb) {. Here I am trying to look at the XML recevied and see if I can match the tags to names in the map (i.e content and web_address) and if so, print the value of the node, associating it to what is it (i.e description or url).
I haven't been able to test this because of this compilation error, which I don't understand because I have refered to node above:
g++ -g -Wall -std=c++11 -I include -o main src/main.cpp include/pugi/pugixml.cpp
src/main.cpp:51:38: error: use of undeclared identifier 'node'
std::cout << "which has value" << node.child_value(kv.second)
My expected output is this:
Found description which has this value Hello!
Found url which has this value www.hotmail.com
From this input
<content>Hello!</content>
<web_address>www.hotmail.com</web_address>
The error message is pretty clear: you didn't define node in the scope of your output code.
When you defined the node in for (auto& node: ...) , it's visible only in the scope of the for loop.
It's not totally clear for me what it should be but I guess you should replace node.child_value(kv.second) with something like eb.second.child_value(kv.second)
I have an STL map with a custom comparator which I want to pass to a function, but the function doesn't recognize the custom comparator.
Trying to access the map within the main function works.
I have listed both attempts in my code.
#include <iostream>
#include <string>
#include <map>
// Error: cmpByStringLength is not recognized (both times)
void funcOut(std::map<std::string, int, cmpByStringLength> myMap)
{
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
}
int main()
{
// Reverse sort by length
struct cmpByStringLength {
bool operator()(const std::string& a, const std::string& b) const {
return a.length() > b.length();
}
};
std::map<std::string, int, cmpByStringLength> myMap;
myMap.emplace("String 1", 5);
myMap.emplace("String 123", 10);
funcOut(myMap);
// Working
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
return 0;
}
You can only use a name after its declaration, and only if it's in scope. Your comparator type is scoped within main, so you can only use it in that function. Move the definition out of main, into the global namespace (or in another namespace if you like), to make it available in other functions.
Alternatively, you could make the other function a template, so it can work with any map type:
template <typename Map>
void funcOut(Map const & myMap) {
// your code here
}
Use a template, because I'm a lazy c++ developer (I don't need to worry about lots of details...) I would do..
template <typename MapType>
void funcOut(MapType& myMap)
{
for (auto& p : myMap)
{
std::cout << p.first << " => " << p.second << std::endl;
}
}