C++ call and definition mismatch - c++

I am looking at this code block at
https://github.com/pytorch/pytorch/blob/master/torch/csrc/autograd/profiler.cpp#L141
pushCallback(
[config](const RecordFunction& fn) {
auto* msg = (fn.seqNr() >= 0) ? ", seq = " : "";
if (config.report_input_shapes) {
std::vector<std::vector<int64_t>> inputSizes;
inputSizes.reserve(fn.inputs().size());
for (const c10::IValue& input : fn.inputs()) {
if (!input.isTensor()) {
inputSizes.emplace_back();
continue;
}
const at::Tensor& tensor = input.toTensor();
if (tensor.defined()) {
inputSizes.push_back(input.toTensor().sizes().vec());
} else {
inputSizes.emplace_back();
}
}
pushRangeImpl(fn.name(), msg, fn.seqNr(), std::move(inputSizes));
} else {
pushRangeImpl(fn.name(), msg, fn.seqNr(), {});
}
},
[](const RecordFunction& fn) {
if (fn.getThreadId() != 0) {
// If we've overridden the thread_id on the RecordFunction, then find
// the eventList that was created for the original thread_id. Then,
// record the end event on this list so that the block is added to
// the correct list, instead of to a new list. This should only run
// when calling RecordFunction::end() in a different thread.
if (state == ProfilerState::Disabled) {
return;
} else {
std::lock_guard<std::mutex> guard(all_event_lists_map_mutex);
const auto& eventListIter =
all_event_lists_map.find(fn.getThreadId());
TORCH_INTERNAL_ASSERT(
eventListIter != all_event_lists_map.end(),
"Did not find thread_id matching ",
fn.getThreadId());
auto& eventList = eventListIter->second;
eventList->record(
EventKind::PopRange,
StringView(""),
fn.getThreadId(),
state == ProfilerState::CUDA);
}
} else {
popRange();
}
},
config.report_input_shapes);
This only has three arguments. But the definition of pushCallback seems to be at this location
https://github.com/pytorch/pytorch/blob/master/torch/csrc/autograd/record_function.cpp#L35 and takes four parameters.
void pushCallback(
RecordFunctionCallback start,
RecordFunctionCallback end,
bool needs_inputs,
bool sampled) {
start_callbacks.push_back(std::move(start));
end_callbacks.push_back(std::move(end));
if (callback_needs_inputs > 0 || needs_inputs) {
++callback_needs_inputs;
}
is_callback_sampled.push_back(sampled);
if (sampled) {
++num_sampled_callbacks;
}
}
I don't know why that function call could work in that way.

If you look at the header you find that it is declared with 4 parameters, out of which the last three have defaults:
TORCH_API void pushCallback(
RecordFunctionCallback start,
RecordFunctionCallback end = [](const RecordFunction&){},
bool needs_inputs = false,
bool sampled = false);
Default arguments only appear on the declaration not on the definition.

Related

I want to combine the list and find(), but I don't know how to merge them

Please see the part where the find() function is called. (I ran it in Visual Studio.)
Code:
using namespace std;
int main(void) {
//Implementing the find()
/*
bool Find(const Stu & a); {
return (*it).name;
}
*/
list<Astu>::iterator that;
//that = astu.begin();
//that = find(astu.begin(), astu.end(), (*it).name);
for (it = stu.begin(); it != stu.end(); it++) {
that = find_if(astu.begin(), astu.end(), (*it).name);
if (that != astu.end()) {
all = astu.erase(all);
all++;
}
else
all++;
}
/*
//Filter absenteeism from the total student roster
for (it = stu.begin(); it != stu.end(); it++) {
for (all = astu.begin(); all != astu.end();) {
if (!strcmp((*all).name, (*it).name)) {
//Delete attendees and latecomers from the list
all = astu.erase(all);
}
else
all++;
}
}
*/
cout << "---------------------\n결석자: " << endl;
//이름순으로 정렬
astu.sort(SizeComp2);
//결석자들 출력
for (all = astu.begin(); all != astu.end(); all++) {
cout << "이름: " << (*all).name << endl;
}
return 0;
}
Output:
C2064 error occurred: Term does not evaluate to a function that takes
1 argument.
Even with find_if() in this code, there is a problem. The bool() part in the comment was used for the find_if object, but it doesn't seem to be used well here.
I deleted the part where there was no problem as a result of debugging. You must use an unconditional list, not a vector.
Where should I fix it?
The third argument to std::find_if is a function.
You could use a lambda as the function:
auto that = find_if(astu.begin(), astu.end(), [it](Astu const& astu)
{
return astu.name == it->name;
});
[This assumes that Astu::name is a std::string]

Got an Error when using C++20 Polymorphism Lambda Function

I'm trying to write a higher-order function via Lambda in C++, and got this code.
void ProcessList::SortCol(std::string col, bool flag) {
auto CmpGenerator = [&]<typename T>
(std::function<T(const Process &itm)> func) {
return (flag? [&](const Process &a, const Process &b) {
return func(a) < func(b);}
: [&](const Process &a, const Process &b) {
return func(a) > func(b);}
);
};
std::function<bool(const Process &a, const Process &b)> cmp;
if (col == "PID") {
cmp = CmpGenerator([](const Process &itm) {
return itm.GetPid();
});
}
else if (col == "CPU") {
cmp = CmpGenerator([](const Process &itm) {
return itm.GetRatioCPU();
});
}
else if (col == "COMMAND") {
cmp = CmpGenerator([](const Process &itm) {
return itm.GetCmd();
});
}
std::sort(lst.begin(), lst.end(), cmp);
}
However when compiling, g++ reported that no match for call to
no match for call to ‘(ProcessList::SortCol(std::string, bool)::<lambda(std::function<T(const Process&)>)>) (ProcessList::SortCol(std::string, bool)::<lambda(const Process&)>)’
What's wrong here with the code?
The primary problem in this example is that a lambda is not a std::function. See this question.
CmpGenerator deduces its argument as std::function<T(Process const&)>, but a lambda will never match that, so deduction fails.
Furthermore, the body of CmpGenerator tries to return one of two different lambdas - which have different types. Those lambdas are not convertible to each other, so the conditional expression will fail. But we also can't deduce the return type of CmpGenerator since the two different lambdas have different types.
We can start by doing this completely by hand. std::ranges::sort takes a projection, which is very helpful in this regard:
if (col == "PID") {
if (increasing) { // <== 'flag' is not a great name
std::ranges::sort(lst, std::less(), &Process::GetPid);
} else {
std::ranges::sort(lst, std::greater(), &Process::GetPid);
}
} else if (col == "CPU") {
// ...
}
This gives the structure that we need to abstract: we're not generating a comparison object, we're generating a call to sort.
That is:
auto sort_by = [&](auto projection){ // <== NB: auto, not std::function
if (increasing) {
std::ranges::sort(lst, std::less(), projection);
} else {
std::ranges::sort(lst, std::greater(), projection);
}
};
if (col == "PID") {
sort_by(&Process::GetPid);
} else if (col == "CPU") {
sort_by(&Process::GetRatioCPU);
} else if (col == "COMMAND") {
sort_by(&Process::GetCmd);
}

Writing a JSON parser for C++

So far I have managed to put together a lexer and a stack in the hopes of achieving a LL1 parser. I am doing this purely to understand how parsing works, and maybe to use these ideas in future projects. I understand there are much better frameworks out there like json-cpp and rapid-json but I would like to understand this for myself.
The header file is give below.
#pragma once
#include <string>
#include <vector>
#include <map>
#include <variant>
#include <fstream>
#include <stack>
#include "Helper.h"
// Debugging
#include <iostream>
// Types to store JSON ouput
struct jlist;
struct jobject;
using json_value = std::variant<int, float, bool, std::string, jlist, jobject>;
enum tag { int_value, float_value, string_value, list, object };
struct jlist {
tag type;
std::vector<json_value *> vector_value;
};
struct jobject {
tag type;
std::map<std::string, json_value *> map_value;
};
class JSONParser
{
public:
JSONParser();
~JSONParser();
void parseFile(std::string);
private:
std::stack<std::string> s;
bool checkDeliminator(char);
std::vector<std::string> lexer(std::ifstream &);
void parser(std::vector<std::string> &);
void transitionTable(std::string cursor);
};
The implementation is as follows.
#include "genetic-optimization/JSONParser.h"
JSONParser::JSONParser() {
}
JSONParser::~JSONParser() = default;
void JSONParser::parseFile(std::string FILE) {
std::ifstream configfile(FILE);
std::vector<std::string> scan = lexer(configfile);
parser(scan);
}
bool JSONParser::checkDeliminator(char piece) {
switch (piece) {
case '[':
return true;
case ']':
return true;
case '{':
return true;
case '}':
return true;
case ':':
return true;
case ',':
return true;
case '"':
return true;
default:
return false;
}
}
std::vector<std::string> JSONParser::lexer(std::ifstream & configfile) {
char piece;
std::string capture = "";
std::string conversion;
std::vector<std::string> capture_list;
while(configfile >> piece) {
if (checkDeliminator(piece)) {
conversion = piece;
if (capture != "") {
capture_list.push_back(capture);
capture_list.push_back(conversion);
capture = "";
} else {
capture_list.push_back(conversion);
}
} else {
capture += piece;
}
}
return capture_list;
}
void JSONParser::parser(std::vector<std::string> & scan) {
for (auto it = scan.begin(); it != scan.end(); ++it) {
std::cout << *it << "\n"; // Make sure the lexer works
transitionTable(*it);
}
}
void JSONParser::transitionTable(std::string cursor) {
if(s.empty()) {
s.push(cursor);
} else {
if (s.top() == "[") {
s.push(cursor);
} else if (s.top() == "]") {
s.pop();
} else if (s.top() == "{") {
s.push(cursor);
} else if (s.top() == "}") {
s.pop();
}
}
}
I am unsure of how to proceed from here but have been using the json grammar as a starting point and the following tutorial for guidance.
json -> element
value -> object|array|string|number|bool|
object -> {}|{members}
members -> member|member,members
member -> string:element
array -> []|[elements]
elements -> element|element,elements
element -> value
I have three main problems.
The JSON grammar seems to have left indirect recursion. Since the grammar is not as simple as that shown in the tutorial I do not know how to eliminate it.
I do not know how to generate the parse table (finite state machine), specifically for something like First(object), what would this be? Is there any resource that has produced a parse table for JSON and might point me in the right direction?
The tutorial seems more to verify that the expression being parsed is produced by the grammar but I would like to store the structure in a variable. Where would this be done and do you have any advice for how this might look in pseudo (or even better C++) code.
For completeness, I am using the following JSON as a test.
[
{
"libraries":[
"terminal",
"binary"
] ,
"functions":[
"terminal-basic",
"binary-basic"
]
}
,
{
"name":"addition",
"type":"binary-basic",
"function":"add_float",
"input":{
"float" : 2
},
"output":"float",
"max-number":2
}
,
{
"name":"exponent",
"type":"binary-basic",
"function":"exponent_float",
"input":{
"float":2
},
"output":"float",
"max-number":2
}
,
{
"name":"exponent",
"type":"binary-basic",
"function":"exponent_float",
"input":{
"float":2,
"int":1
},
"output":"float",
"max-number":1
}
,
{
"name":"constant_1",
"type":"terminal-basic",
"function":"non_random_constant",
"value":0.5,
"input":{ },
"output":"float",
"max-number":3
}
,
{
"name":"constant_2",
"type":"terminal-basic",
"function":"non_random_constant",
"value":2.0,
"input":{ },
"output":"float",
"max-number":3
}
,
{
"name":"constant_3",
"type":"terminal-basic",
"function":"non_random_constant",
"value":true,
"input":{
"bool":1
},
"output":"bool",
"max-number":1
}
]
I wouldn't like to leave this question unanswered for anyone coming here in the future, however, I am personally not a big fan of the code that accompanies this answer. It feels inefficient, not particularly elegant and I am unsure if it represents the theoretical model I was trying to implement in the first place. I took my lead from #MSalters comment, which to me meant build something that works and worry if the model is theoretically sound later. Below is my attempt.
The header adds a few more functions. Many of them purely to assist fsm and parser.
class JSONParser
{
public:
JSONParser();
~JSONParser();
void parseFile(std::string);
private:
json_value root;
std::stack<std::string> s;
std::stack<json_value> s_value;
// Lexer
bool checkDeliminator(char);
std::vector<std::string> lexer(std::ifstream &);
// FSM varaibles
enum state { int_value, float_value, bool_value, string_value, default_value, bad_state};
state current;
// FSM
void fsm(std::string);
// Parser variables
enum stack_map { list_open, list_close, object_open, object_close, colon, comma, buffer, follow};
std::map<std::string, stack_map> stack_conversion;
// Parser helper functions
template<typename T> void addElement();
template<typename T> void insert(std::string &, T (*)(const std::string &));
template<typename T> void insert();
void insert(std::string &);
void pushBuffer();
template<typename ... T> bool multiComparision(const char scope, T ... args);
bool isDigit(const char);
static int st2i(const std::string & value);
static float st2f(const std::string & value);
static bool st2b(const std::string & value);
// Parser
void parser(const std::string & cursor);
};
The implementation file follows.
#include "genetic-optimization/JSONParser.h"
JSONParser::JSONParser() {
state current = default_value;
stack_conversion = { { "[", list_open }, { "]", list_close }, { "{", object_open }, { "}", object_close }, { ":", colon }, { ",", comma }, { "buffer", buffer } };
}
JSONParser::~JSONParser() = default;
void JSONParser::parseFile(std::string FILE) {
std::ifstream configfile(FILE);
std::vector<std::string> scan = lexer(configfile);
scan.push_back("terminate");
for (auto it = scan.begin(); it != scan.end(); ++it) {
parser(*it);
}
root = s_value.top();
s_value.pop();
}
// Lexer
bool JSONParser::checkDeliminator(char piece) {
switch (piece) {
case '[':
return true;
case ']':
return true;
case '{':
return true;
case '}':
return true;
case ':':
return true;
case ',':
return true;
default:
return false;
}
}
std::vector<std::string> JSONParser::lexer(std::ifstream & configfile) {
char piece;
std::string capture = "";
std::string conversion;
std::vector<std::string> capture_list;
while(configfile >> piece) {
if (checkDeliminator(piece)) {
conversion = piece;
if (capture != "") {
capture_list.push_back(capture);
capture_list.push_back(conversion);
capture = "";
} else {
capture_list.push_back(conversion);
}
} else {
capture += piece;
}
}
return capture_list;
}
// FSM
void JSONParser::fsm(std::string value) {
current = default_value;
char point;
auto it = value.begin();
while (it != value.end()) {
point = *it;
if (point == '"' & current == default_value) {
current = string_value;
return;
} else if (isdigit(point)) {
if (current == default_value | current == int_value) {
current = int_value;
++it;
} else if (current == float_value) {
++it;
} else {
current = bad_state;
return;
}
} else if (point == '.' & current == int_value) {
current = float_value;
++it;
} else if (point == 'f' & current == float_value) {
++it;
} else if (current == default_value) {
if (value == "true" | value == "false") {
current = bool_value;
return;
} else {
current = bad_state;
return;
}
} else {
current = bad_state;
return;
}
}
}
// Parser Helper functions
template<>
void JSONParser::addElement<jobject>() {
json_value value_read;
json_value key_read;
value_read = s_value.top();
s_value.pop();
key_read = s_value.top();
s_value.pop();
std::get<jobject>(s_value.top()).insert(key_read, value_read);
}
template<>
void JSONParser::addElement<jlist>() {
json_value value_read;
value_read = s_value.top();
s_value.pop();
std::get<jlist>(s_value.top()).push_back(value_read);
}
template<typename T>
void JSONParser::insert(std::string & value, T (*fptr)(const std::string &)) {
T T_value(fptr(value));
s_value.push(T_value);
}
template<typename T>
void JSONParser::insert() {
T T_value;
s_value.push(T_value);
}
void JSONParser::insert(std::string & value) {
value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
s_value.push(value);
}
void JSONParser::pushBuffer() {
s.pop();
s.push("buffer");
}
template<typename ... T>
bool JSONParser::multiComparision(const char scope, T ... args) {
return (scope == (args || ...));
}
bool JSONParser::isDigit(const char c) {
return multiComparision<char>(c, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
}
int JSONParser::st2i(const std::string & value) {
return stoi(value);
}
float JSONParser::st2f(const std::string & value) {
return stof(value);
}
bool JSONParser::st2b(const std::string & value) {
if (value == "true") {
return true;
} else {
return false;
}
}
// Parser
void JSONParser::parser(const std::string & cursor) {
if(s.empty()) {
s.push(cursor);
} else {
stack_map stack_value;
std::string value = s.top();
if (stack_conversion.find(value) != stack_conversion.end()) {
stack_value = stack_conversion[s.top()];
} else {
stack_value = follow;
}
switch (stack_value) {
case buffer:
s.pop();
break;
case list_open:
insert<jlist>();
if (cursor == "]") {
pushBuffer();
return;
}
break;
case list_close:
addElement<jlist>();
s.pop();
s.pop();
break;
case object_open:
insert<jobject>();
if (cursor == "}") {
pushBuffer();
return;
}
break;
case object_close:
addElement<jobject>();
s.pop();
s.pop();
break;
case colon:
s.pop();
break;
case comma:
s.pop();
if (s.top() == "{") {
addElement<jobject>();
} else {
addElement<jlist>();
}
break;
default:
s.pop();
fsm(value);
switch (current) {
case string_value:
insert(value);
break;
case int_value:
insert<int>(value, st2i);
break;
case float_value:
insert<float>(value, st2f);
break;
case bool_value:
insert<bool>(value, st2b);
break;
default:
std::cout << "Bad state\n";
}
}
s.push(cursor);
}
}
The idea was to have the lexer break at each deliminator and place all the generated tokens into a vector. This vector called scan could then be looped through. At each iteration of this loop parser would be run. In general this reads the top of the stack s and determines whether a bracket/brace is opening or closing or a terminal value has been reached. If a bracket/brace is opening, a new jobject or jlist is generated and placed onto a new stack s_value, if a terminal value is reached fsm (finite state machine) runs and determines the type of value and places it on top of s_value, should a comma or closing bracket be reached the appropriate values are moved off the stack and the elements in s_value are inserted into their appropriate containers.
The biggest meatball in this spaghetti is how elements in the JSON tree are called.
std::cout << std::get<bool>(std::get<jobject>(std::get<jobject>(std::get<jlist>(root)[6])["input"])["bool"]); // Should return 1
While this does indeed return 1. The nested std::get calls seem just plain wrong and I'm not sure if they can be incorporated into the operator [] or through (sigh) a third stack that tracks the type of object being stored.
This was my basic attempt, it's not pretty but it does work. Hopefully I can refine it further and improve on what I have.
I am not an expert at parsing so my answer would be very heuristic...
JSON grammar is simple. I believe we do not need to try to understand of follow over-specified (E)BNF form to actually parse JSON string. Try to write your own simple form. After you do that, you may feel a need for a better form. Then you can re-try to fully understand why there are such grammars.
Isn't FSM simply "you have to do that in this state?" States are preferably managed by a stack (not like you have to have an instance whose members indicate states like an abstract figure in text book in many cases of the real world) and you will do what you have to do in loops based on a top state of the stack. I believe you do not need an instance of 'parse table.' Can it be abstract or pervasively exists somewhere in code?
I also started to practice parsing with JSON. Please check my single header file.
I used 7 stack statuses:
enum status {
READING_OBJECT_KEY,
READ_OBJECT_KEY,
READING_OBJECT_VALUE, READING_ARRAY_VALUE,
READ_OBJECT_VALUE, READ_ARRAY_VALUE, READ_OTHER_VALUE
};
Heuristically, I started actual parsing after skipping preceding whitespace and check the first non-whitespace character:
} else if (p.c == '{') {
p.ps.push(json::parsing::READING_OBJECT_KEY);
j = json::object();
p.js.push(j.v);
break;
} else if (p.c == '[') {
p.ps.push(json::parsing::READING_ARRAY_VALUE);
j = json::array();
p.js.push(j.v);
break;
}
Then I actually started to parse with 8 functions:
while (p.iss.get(p.c)) {
p.i++;
if (p.c == ' ' ) {}
else if (p.c == '{' ) json::parse__left_brace(p);
else if (p.c == '}' ) json::parse__right_brace(p);
else if (p.c == '[' ) json::parse__left_bracket(p);
else if (p.c == ']' ) json::parse__right_bracket(p);
else if (p.c == ':' ) json::parse__colon(p);
else if (p.c == ',' ) json::parse__comma(p);
else if (p.c == '\"') json::parse__quote(p);
else json::parse__else(p);
}

Am I using function pointers correctly?

I have a function that looks something like this in pseudocode:
std::string option = "option1" // one of n options, user supplied
for (int i = 0; i < 100000; i++) {
if (option == "option1") {
doFunction1a();
} else if (option == "option2") {
doFunction2a();
} else if (option == "option3") {
doFunction3a();
}
// more code...
if (option == "option1") {
doFunction1b();
} else if (option == "option2") {
doFunction2b();
} else if (option == "option3") {
doFunction3b();
}
}
However, I could avoid the repeated if statement inside the loop by doing something like this:
std::string option = "option1" // one of n options, user supplied
int (*doFunctiona)(int, int);
int (*doFunctionb)(int, int);
if (option == "option1") {
doFunctiona = doFunction1a;
doFunctionb = doFunction1b;
} else if (option == "option2") {
doFunctiona = doFunction2a;
doFunctionb = doFunction2b;
} else if (option == "option3") {
doFunctiona = doFunction3a;
doFunctionb = doFunction3b;
}
for (int i = 0; i < 100000; i++) {
doFunctiona();
// more code...
doFunctionb();
}
I realize that this will have little effect on performance (the time spend by the functions dominates the time it takes to execute the if statement).
However, In terms of "good coding practices", is this a good way to set up variable function calling? With "good" I mean: (1) easily expandable, there could easily be 20 options in the future; 2) results in readable code. I'm hoping there exists some kind of standard method for accomplishing this. If not, feel free to close as opinion based.
Just use an unordered_map and spare yourself the if-else-if-orgy:
std::unordered_map<std::string, std::vector<int (*)(int, int)>> functions;
functions.insert({ "option1", { doFunction1a, doFunction1b } });
...
const auto& vec = functions["option1"];
for(auto& f : vec) f(1, 2);
Beside using map I recommend to use std::function and lambdas which will give you more flexibility and syntax is more friendly (at least for me):
std::unordered_map<std::string, std::function<void()>> functions {
{
"option1",
[] {
functionA();
functionB();
}
},
{
"option2",
[] {
functionC();
functionD();
}
}
};
auto optionFuncIt = functions.find("option1");
if (optionFuncIt != functions.end()) {
optionFuncIt->second();
} else {
std::cerr << "Invalid option name" << std::endl;
}

Find a specific node in a XML document with TinyXML

I want to parse some data from an xml file with TinyXML.
Here's my text.xml file content:
<?xml version="1.0" encoding="iso-8859-1"?>
<toto>
<tutu>
<tata>
<user name="toto" pass="13" indice="1"/>
<user name="tata" pass="142" indice="2"/>
<user name="titi" pass="azerty" indice="1"/>
</tata>
</tutu>
</toto>
I want to access to the first element 'user'. The way to do this is the following :
TiXmlDocument doc("test.xml");
if (doc.LoadFile())
{
TiXmlNode *elem = doc.FirstChildElement()->FirstChildElement()->FirstChildElement()->FirstChildElement();
std::cout << elem->Value() << std::endl;
}
In output : user.
But the code is pretty ugly and not generic. I tried the code below to simulate the same behaviour than the code above but it doesn't work and an error occured.
TiXmlElement *getElementByName(TiXmlDocument &doc, std::string const &elemt_value)
{
TiXmlElement *elem = doc.FirstChildElement(); //Tree root
while (elem)
{
if (!std::string(elem->Value()).compare(elemt_value))
return (elem);
elem = elem->NextSiblingElement();
}
return (NULL);
}
Maybe I missed a special function in the library which can do this work (a getElementByName function). I just want to get a pointer to the element where the value is the one I'm looking for. Does anyone can help me? Thanks in advance for your help.
Try this
TiXmlElement * getElementByName(TiXmlDocument & doc, std::string const & elemt_value) {
TiXmlElement * elem = doc.RootElement(); //Tree root
while (elem) {
if (!std::string(elem - > Value()).compare(elemt_value)) return (elem);
/*elem = elem->NextSiblingElement();*/
if (elem - > FirstChildElement()) {
elem = elem - > FirstChildElement();
} else if (elem - > NextSiblingElement()) {
elem = elem - > NextSiblingElement();
} else {
while (!elem - > Parent() - > NextSiblingElement()) {
if (elem - > Parent() - > ToElement() == doc.RootElement()) {
return NULL;
}
elem = elem - > Parent() - > NextSiblingElement();
}
}
}
return (NULL);
}
Adi's answer didn't work when i just copy pasted it into my code,
but i modified it and now it works fine for me.
since i made quite a lot of changes i thought i should post my final code here.
void parseXML(tinyxml2::XMLDocument& xXmlDocument, std::string sSearchString, std::function<void(tinyxml2::XMLNode*)> fFoundSomeElement)
{
if ( xXmlDocument.ErrorID() != tinyxml2::XML_SUCCESS )
{
// XML file is not ok ... we throw some exception
throw DataReceiverException( "XML file parsing failed" );
} // if
//ispired by http://stackoverflow.com/questions/11921463/find-a-specific-node-in-a-xml-document-with-tinyxml
tinyxml2::XMLNode * xElem = xXmlDocument.FirstChild();
while(xElem)
{
if (xElem->Value() && !std::string(xElem->Value()).compare(sSearchString))
{
fFoundSomeElement(xElem);
}
/*
* We move through the XML tree following these rules (basically in-order tree walk):
*
* (1) if there is one or more child element(s) visit the first one
* else
* (2) if there is one or more next sibling element(s) visit the first one
* else
* (3) move to the parent until there is one or more next sibling elements
* (4) if we reach the end break the loop
*/
if (xElem->FirstChildElement()) //(1)
xElem = xElem->FirstChildElement();
else if (xElem->NextSiblingElement()) //(2)
xElem = xElem->NextSiblingElement();
else
{
while(xElem->Parent() && !xElem->Parent()->NextSiblingElement()) //(3)
xElem = xElem->Parent();
if(xElem->Parent() && xElem->Parent()->NextSiblingElement())
xElem = xElem->Parent()->NextSiblingElement();
else //(4)
break;
}//else
}//while
}
(for completeness) example how to call the function:
tinyxml2::XMLDocument xXmlDocument;
xXmlDocument.Parse(sXmlDocument.c_str());
parseXML(xXmlDocument, "user",[](tinyxml2::XMLNode* xElem)
{
int iPass;
xElem->QueryIntAttribute( "pass", &iPass );
std::cout << iPass << "\n";
});
You can also iterate through your XML elements one-by-one by using recursive function combined with lamda-function as a handler.
//
// This function will iterate through your XML tree and call the 'parseElement' function for each found element.
//
void RecursiveXMLParse(TiXmlElement* element, std::function<void(TiXmlElement*)>& parseElement)
{
if (element != nullptr)
{
parseElement(element);
auto child = element->FirstChildElement();
if (child != nullptr)
{
RecursiveXMLParse(child, parseElement);
}
for (auto sibling = element->NextSiblingElement(); sibling != nullptr; sibling = sibling->NextSiblingElement())
{
RecursiveXMLParse(sibling, parseElement);
}
}
}
Usage: Just pass the XML root element, and your data handler lambda function to the recursive Parser-function.
int main()
{
//
// Define your data handler labmda
//
std::function<void(TiXmlElement*)>parseElement = [&](TiXmlElement* e) -> void
{
if (std::string(elem->Value()).compare("user"))
{
// Parse your user data
}
};
// Pass the root element along with the above defined lambda to the recursive function
RecursiveXMLParse(doc.RootElement(), parseElement);
return 0;
}
XMLElement *getElementByName(XMLDocument &ele, std::string const &elemt_value)
{
XMLElement *elem = ele.FirstChildElement(); //Tree root
while (elem)
{
if (!std::string(elem->Value()).compare(elemt_value))
return elem;
if (elem->FirstChildElement())
{
elem = elem->FirstChildElement();
}
else if (elem->NextSiblingElement())
{
elem = elem->NextSiblingElement();
}
else
{
if (elem->Parent()->ToElement()->NextSiblingElement())
{
elem = elem->Parent()->ToElement()->NextSiblingElement();
}
else if (elem->Parent()->ToElement()->FirstChildElement()
&& strcmp(elem->Name(), elem->Parent()->ToElement()->FirstChildElement()->Name()))
{
elem = elem->Parent()->ToElement()->FirstChildElement();
}
else {
break;
}
}
}
return NULL;
}
// A small tweak in the above given solution