xml_utils.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2024 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 
10 #include "xml_utils.h"
11 
12 #include <mrpt/core/bits_math.h>
13 #include <mrpt/core/format.h>
14 #include <mrpt/core/get_env.h>
15 #include <mrpt/img/TColor.h>
16 #include <mrpt/io/vector_loadsave.h> // file_get_contents()
17 #include <mrpt/math/TPolygon2D.h>
18 #include <mrpt/poses/CPose2D.h>
19 #include <mrpt/poses/CPose3D.h>
20 #include <mrpt/system/COutputLogger.h>
21 #include <mrpt/system/filesystem.h>
22 #include <mrpt/system/string_utils.h>
23 #include <mvsim/World.h>
24 #include <mvsim/basic_types.h>
25 
26 #include <cstdio>
27 #include <rapidxml_print.hpp>
28 #include <rapidxml_utils.hpp>
29 #include <sstream> // std::stringstream
30 
31 #include "parse_utils.h"
32 
33 using namespace rapidxml;
34 using namespace mvsim;
35 
41  const std::string& inStr, const std::string& varName,
42  const std::map<std::string, std::string>& variableNamesValues,
43  const char* functionNameContext) const
44 {
45  const std::string str = mvsim::parse(inStr, variableNamesValues);
46 
47  // Special cases:
48  // "%s" ==> std::strings
49  if (std::string(frmt) == std::string("%s"))
50  {
51  std::string& val2 = *reinterpret_cast<std::string*>(val);
52  val2 = mrpt::system::trim(str);
53  }
54  // "%lf_deg" ==> mrpt::DEG2RAD()
55  else if (std::string(frmt) == std::string("%lf_deg"))
56  {
57  if (1 != ::sscanf(str.c_str(), frmt, val))
58  throw std::runtime_error(mrpt::format(
59  "%s Error parsing attribute '%s'='%s' (Expected "
60  "format:'%s')",
61  functionNameContext, varName.c_str(), str.c_str(), frmt));
62  double& ang = *reinterpret_cast<double*>(val);
63  ang = mrpt::DEG2RAD(ang);
64  }
65  // "%bool" ==> bool*
66  else if (std::string(frmt) == std::string("%bool"))
67  {
68  bool& bool_val = *reinterpret_cast<bool*>(val);
69 
70  const std::string sStr = mrpt::system::lowerCase(mrpt::system::trim(std::string(str)));
71  if (sStr == "1" || sStr == "true")
72  bool_val = true;
73  else if (sStr == "0" || sStr == "false")
74  bool_val = false;
75  else
76  throw std::runtime_error(mrpt::format(
77  "%s Error parsing 'bool' attribute '%s'='%s' (Expected "
78  "'true' or 'false')",
79  functionNameContext, varName.c_str(), str.c_str()));
80  }
81  // "%color" ==> mrpt::img::TColor
82  else if (std::string(frmt) == std::string("%color"))
83  {
84  // HTML-like format:
85  if (!(str.size() > 1 && str[0] == '#'))
86  throw std::runtime_error(mrpt::format(
87  "%s Error parsing '%s'='%s' (Expected "
88  "format:'#RRGGBB[AA]')",
89  functionNameContext, varName.c_str(), str.c_str()));
90 
91  unsigned int r, g, b, a = 0xff;
92  int ret = ::sscanf(str.c_str() + 1, "%2x%2x%2x%2x", &r, &g, &b, &a);
93  if (ret != 3 && ret != 4)
94  throw std::runtime_error(mrpt::format(
95  "%s Error parsing '%s'='%s' (Expected "
96  "format:'#RRGGBB[AA]')",
97  functionNameContext, varName.c_str(), str.c_str()));
98  mrpt::img::TColor& col = *reinterpret_cast<mrpt::img::TColor*>(val);
99  col = mrpt::img::TColor(r, g, b, a);
100  }
101  // "%pose2d"
102  // "%pose2d_ptr3d"
103  else if (!strncmp(frmt, "%pose2d", strlen("%pose2d")))
104  {
105  double x, y, yaw;
106  int ret = ::sscanf(str.c_str(), "%lf %lf %lf", &x, &y, &yaw);
107  if (ret != 3)
108  throw std::runtime_error(mrpt::format(
109  "%s Error parsing '%s'='%s' (Expected format:'X Y "
110  "YAW_DEG')",
111  functionNameContext, varName.c_str(), str.c_str()));
112 
113  // User provides angles in deg:
114  yaw = mrpt::DEG2RAD(yaw);
115 
116  const mrpt::poses::CPose2D p(x, y, yaw);
117 
118  // Sub-cases:
119  if (!strcmp(frmt, "%pose2d"))
120  {
121  mrpt::poses::CPose2D& pp = *reinterpret_cast<mrpt::poses::CPose2D*>(val);
122  pp = p;
123  }
124  else if (!strcmp(frmt, "%pose2d_ptr3d"))
125  {
126  mrpt::poses::CPose3D& pp = *reinterpret_cast<mrpt::poses::CPose3D*>(val);
127  pp = mrpt::poses::CPose3D(p);
128  }
129  else
130  throw std::runtime_error(
131  mrpt::format("%s Error: Unknown format specifier '%s'", functionNameContext, frmt));
132  }
133  // %point3d
134  else if (!strncmp(frmt, "%point3d", strlen("%point3d")))
135  {
136  double x = 0, y = 0, z = 0;
137  int ret = ::sscanf(str.c_str(), "%lf %lf %lf", &x, &y, &z);
138  if (ret != 2 && ret != 3)
139  throw std::runtime_error(mrpt::format(
140  "%s Error parsing '%s'='%s' (Expected format:'X Y [Z]')", functionNameContext,
141  varName.c_str(), str.c_str()));
142 
143  mrpt::math::TPoint3D& pp = *reinterpret_cast<mrpt::math::TPoint3D*>(val);
144 
145  pp.x = x;
146  pp.y = y;
147  pp.z = z;
148  }
149  // "%pose3d"
150  else if (!strncmp(frmt, "%pose3d", strlen("%pose3d")))
151  {
152  double x, y, z, yawDeg, pitchDeg, rollDeg;
153  int ret = ::sscanf(
154  str.c_str(), "%lf %lf %lf %lf %lf %lf", &x, &y, &z, &yawDeg, &pitchDeg, &rollDeg);
155  if (ret != 6)
156  throw std::runtime_error(mrpt::format(
157  "%s Error parsing '%s'='%s' (Expected format:'X Y Z"
158  "YAW_DEG PITCH_DEG ROLL_DEG')",
159  functionNameContext, varName.c_str(), str.c_str()));
160 
161  // User provides angles in deg:
162  const auto yaw = mrpt::DEG2RAD(yawDeg);
163  const auto pitch = mrpt::DEG2RAD(pitchDeg);
164  const auto roll = mrpt::DEG2RAD(rollDeg);
165 
166  const mrpt::poses::CPose3D p(x, y, yaw);
167 
168  mrpt::poses::CPose3D& pp = *reinterpret_cast<mrpt::poses::CPose3D*>(val);
169  pp = mrpt::poses::CPose3D(x, y, z, yaw, pitch, roll);
170  }
171  else
172  {
173  // Generic parse:
174  if (1 != ::sscanf(str.c_str(), frmt, val))
175  throw std::runtime_error(mrpt::format(
176  "%s Error parsing attribute '%s'='%s' (Expected "
177  "format:'%s')",
178  functionNameContext, varName.c_str(), str.c_str(), frmt));
179  }
180 }
181 
184  const std::map<std::string, std::string>& variableNamesValues, const char* functionNameContext)
185 {
186  for (const auto& param : params)
187  {
188  if (auto attr = xml_node.first_attribute(param.first.c_str()); attr && attr->value())
189  {
190  param.second.parse(
191  attr->value(), attr->name(), variableNamesValues, functionNameContext);
192  }
193  }
194 }
195 
198  const std::map<std::string, std::string>& variableNamesValues, const char* functionNameContext)
199 {
200  if (auto it_param = params.find(xml_node.name()); it_param != params.end())
201  {
202  // parse parameter:
203  it_param->second.parse(
204  xml_node.value(), xml_node.name(), variableNamesValues, functionNameContext);
205  return true;
206  }
207  else
208  {
209  return false;
210  }
211 }
212 
216  const rapidxml::xml_node<char>& root, const TParameterDefinitions& params,
217  const std::map<std::string, std::string>& variableNamesValues, const char* functionNameContext,
218  mrpt::system::COutputLogger* logger)
219 {
220  rapidxml::xml_node<>* node = root.first_node();
221  while (node)
222  {
223  bool recognized =
224  parse_xmlnode_as_param(*node, params, variableNamesValues, functionNameContext);
225  if (!recognized && logger)
226  {
227  logger->logFmt(
228  mrpt::system::LVL_WARN, "Unrecognized tag '<%s>' in %s", node->name(),
229  functionNameContext ? functionNameContext : "(none)");
230  }
231  node = node->next_sibling(nullptr); // Move on to next node
232  }
233 }
234 
235 mrpt::math::TPose2D mvsim::parseXYPHI(
236  const std::string& sOrg, bool allow_missing_angle, double default_angle_radians,
237  const std::map<std::string, std::string>& variableNamesValues)
238 {
239  mrpt::math::TPose2D v;
240  v.phi = mrpt::RAD2DEG(default_angle_radians); // Default ang.
241 
242  const auto s = parse(sOrg, variableNamesValues);
243  int na = ::sscanf(s.c_str(), "%lf %lf %lf", &v.x, &v.y, &v.phi);
244 
245  // User provides numbers as degrees:
246  v.phi = mrpt::DEG2RAD(v.phi);
247 
248  if ((na != 3 && !allow_missing_angle) || (na != 2 && na != 3 && allow_missing_angle))
249  throw std::runtime_error(mrpt::format("Malformed pose string: '%s'", s.c_str()));
250 
251  return v;
252 }
253 
259  const rapidxml::xml_node<char>& xml_node, mrpt::math::TPolygon2D& out_poly,
260  const char* functionNameContext)
261 {
262  out_poly.clear();
263 
264  for (rapidxml::xml_node<char>* pt_node = xml_node.first_node("pt"); pt_node;
265  pt_node = pt_node->next_sibling("pt"))
266  {
267  if (!pt_node->value())
268  throw std::runtime_error(
269  mrpt::format("%s Error: <pt> node seems empty.", functionNameContext));
270 
271  mrpt::math::TPoint2D pt;
272  const char* str_val = pt_node->value();
273  if (2 != ::sscanf(str_val, "%lf %lf", &pt.x, &pt.y))
274  throw std::runtime_error(mrpt::format(
275  "%s Error parsing <pt> node: '%s' (Expected format:'<pt>X "
276  "Y</pt>')",
277  functionNameContext, str_val));
278 
279  out_poly.push_back(pt);
280  }
281 
282  if (out_poly.size() < 3)
283  throw std::runtime_error(mrpt::format(
284  "%s Error: <shape> node requires 3 or more <pt>X Y</pt> "
285  "entries.",
286  functionNameContext));
287 }
288 
289 std::tuple<std::shared_ptr<rapidxml::xml_document<>>, rapidxml::xml_node<>*>
290  mvsim::readXmlTextAndGetRoot(const std::string& xmlData, const std::string& pathToFile)
291 {
292  using namespace rapidxml;
293  using namespace std::string_literals;
294 
295  // Parse:
296  char* input_str = const_cast<char*>(xmlData.c_str());
297  auto xml = std::make_shared<rapidxml::xml_document<>>();
298  try
299  {
300  xml->parse<0>(input_str);
301  }
302  catch (rapidxml::parse_error& e)
303  {
304  unsigned int line = static_cast<long>(std::count(input_str, e.where<char>(), '\n') + 1);
305  throw std::runtime_error(
306  mrpt::format("XML parse error (Line %u): %s", static_cast<unsigned>(line), e.what()));
307  }
308 
309  // Sanity check:
310  xml_node<>* root = xml->first_node();
311  ASSERTMSG_(root, "XML parse error: No root node found (empty file '"s + pathToFile + "'?)"s);
312  return {xml, root};
313 }
314 
315 std::tuple<XML_Doc_Data::Ptr, rapidxml::xml_node<>*> mvsim::readXmlAndGetRoot(
316  const std::string& pathToFile, const std::map<std::string, std::string>& variables,
317  const std::set<std::string>& varsRetain)
318 {
319  using namespace rapidxml;
320  using namespace std::string_literals;
321 
322  ASSERT_FILE_EXISTS_(pathToFile);
323 
324  auto xmlDoc = std::make_shared<XML_Doc_Data>();
325 
326  // Special variables:
327  std::map<std::string, std::string> childVariables = variables;
328  childVariables["MVSIM_CURRENT_FILE_DIRECTORY"] = mrpt::system::toAbsolutePath(
329  mrpt::system::extractFileDirectory(pathToFile), false /*canonical*/);
330 
331  xmlDoc->documentData =
332  mvsim::parse_variables(mrpt::io::file_get_contents(pathToFile), childVariables, varsRetain);
333 
334  auto [xml, root] = readXmlTextAndGetRoot(xmlDoc->documentData, pathToFile);
335 
336  xmlDoc->doc = std::move(xml);
337 
338  return {xmlDoc, root};
339 }
340 
341 static std::string::size_type findClosing(
342  size_t pos, const std::string& s, const char searchEndChar, const char otherStartChar)
343 {
344  int openEnvs = 1;
345  for (; pos < s.size(); pos++)
346  {
347  const char ch = s[pos];
348  if (ch == otherStartChar)
349  openEnvs++;
350  else if (ch == searchEndChar)
351  {
352  openEnvs--;
353  if (openEnvs == 0)
354  {
355  return pos;
356  }
357  }
358  }
359 
360  // not found:
361  return std::string::npos;
362 }
363 
364 // "foo|bar" -> {"foo","bar"}
365 static std::tuple<std::string, std::string> splitVerticalBar(const std::string& s)
366 {
367  const auto posBar = s.find("|");
368  if (posBar == std::string::npos) return {s, {}};
369 
370  return {s.substr(0, posBar), s.substr(posBar + 1)};
371 }
372 
373 static std::string parseVars(
374  const std::string& text, const std::map<std::string, std::string>& variables,
375  const std::set<std::string>& varsRetain, const size_t searchStartPos = 0)
376 {
377  MRPT_TRY_START
378 
379  const auto start = text.find("${", searchStartPos);
380  if (start == std::string::npos) return text;
381 
382  const std::string pre = text.substr(0, start);
383  const std::string post = text.substr(start + 2);
384 
385  const auto post_end = findClosing(0, post, '}', '{');
386  if (post_end == std::string::npos)
387  {
388  THROW_EXCEPTION_FMT(
389  "Column=%u: Cannot find matching `}` for `${` in: `%s`",
390  static_cast<unsigned int>(start), text.c_str());
391  }
392 
393  const auto varnameOrg = post.substr(0, post_end);
394 
395  const auto [varname, defaultValue] = splitVerticalBar(varnameOrg);
396 
397  if (varsRetain.count(varname) != 0)
398  {
399  // Skip replacing this one:
400  return parseVars(text, variables, varsRetain, start + 2);
401  }
402 
403  std::string varvalue;
404  if (auto itVal = variables.find(varname); itVal != variables.end())
405  {
406  varvalue = itVal->second;
407  }
408  else if (const char* v = ::getenv(varname.c_str()); v != nullptr)
409  {
410  varvalue = std::string(v);
411  }
412  else
413  {
414  if (!defaultValue.empty())
415  {
416  varvalue = defaultValue;
417  }
418  else
419  {
420  THROW_EXCEPTION_FMT("mvsim::parseVars(): Undefined variable: ${%s}", varname.c_str());
421  }
422  }
423 
424  return parseVars(pre + varvalue + post.substr(post_end + 1), variables, varsRetain);
425  MRPT_TRY_END
426 }
427 
429  const std::string& in, const std::map<std::string, std::string>& variables,
430  const std::set<std::string>& varsRetain)
431 {
432  const auto ret = parseVars(in, variables, varsRetain);
433 
434  thread_local const bool MVSIM_VERBOSE_PARSE = mrpt::get_env<bool>("MVSIM_VERBOSE_PARSE", false);
436  {
437  std::cout << "[parse_variables] Input:\n" << in << "\n";
438  std::cout << "[parse_variables] Output:\n" << ret << "\n";
439  std::cout << "[parse_variables] variables: ";
440  for (const auto& kv : variables) std::cout << kv.first << ",";
441  std::cout << "\n";
442  }
443 
444  return ret;
445 }
446 
448  const World& parent, const rapidxml::xml_node<char>* n, const std::set<std::string>& varsRetain,
449  std::stringstream& ss)
450 {
451  // TAG: <include>
452  if (strcmp(n->name(), "include") == 0)
453  {
454  auto fileAttrb = n->first_attribute("file");
455  ASSERTMSG_(fileAttrb, "XML tag '<include />' must have a 'file=\"xxx\"' attribute)");
456 
457  const std::string relFile =
458  mvsim::parse(fileAttrb->value(), parent.user_defined_variables());
459  const auto absFile = parent.local_to_abs_path(relFile);
460  parent.logStr(
461  mrpt::system::LVL_DEBUG,
462  mrpt::format("XML parser: including file: '%s'", absFile.c_str()));
463 
464  std::map<std::string, std::string> vars;
465  // Inherit the user-defined variables from parent scope
466  vars = parent.user_defined_variables();
467  // Plus new ones:
468  for (auto attr = n->first_attribute(); attr; attr = attr->next_attribute())
469  {
470  if (strcmp(attr->name(), "file") == 0) continue;
471  vars[attr->name()] = attr->value();
472  }
473 
474  const auto [xml, nRoot] = readXmlAndGetRoot(absFile, vars, varsRetain);
475  (void)xml;
476 
477  ss << "<!-- INCLUDE: '" << absFile << "' -->\n";
478  recursive_xml_to_str_solving_includes(parent, nRoot, varsRetain, ss);
479  }
480  // TAG: <if>
481  else if (strcmp(n->name(), "if") == 0)
482  {
483  bool isTrue = parent.evaluate_tag_if(*n);
484  if (!isTrue) return;
485 
486  for (auto childNode = n->first_node(); childNode; childNode = childNode->next_sibling())
487  {
488  recursive_xml_to_str_solving_includes(parent, childNode, varsRetain, ss);
489  }
490  }
491  else
492  {
493  // anything else: just print as is:
494  ss << *n;
495  }
496 }
497 
499  const World& parent, const rapidxml::xml_node<char>* xml_node,
500  const std::set<std::string>& varsRetain)
501 {
502  // rapidxml doesn't allow making copied of objects.
503  // So: convert to txt; then re-parse later on.
504  // Also, this allow us to solve "include"s.
505  std::stringstream ss;
506 
507  // Parent opening tag + attributes:
508  // "<vehicle:class name="foo">"
509  ss << "<" << xml_node->name();
510  for (auto a = xml_node->first_attribute(); a; a = a->next_attribute())
511  ss << " " << a->name() << "=\"" << a->value() << "\"";
512  ss << ">\n";
513 
514  // Solve includes:
515  for (auto n = xml_node->first_node(); n; n = n->next_sibling())
516  {
517  recursive_xml_to_str_solving_includes(parent, n, varsRetain, ss);
518  }
519 
520  // "</vehicle:class>"
521  ss << "</" << xml_node->name() << ">\n";
522 
523  return ss.str();
524 }
mvsim
Definition: Client.h:21
parse_utils.h
MVSIM_VERBOSE_PARSE
const thread_local bool MVSIM_VERBOSE_PARSE
Definition: parse_utils.cpp:26
basic_types.h
s
XmlRpcServer s
mvsim::parse_xmlnode_children_as_param
void parse_xmlnode_children_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="", mrpt::system::COutputLogger *logger=nullptr)
Definition: xml_utils.cpp:215
mvsim::xml_to_str_solving_includes
std::string xml_to_str_solving_includes(const World &parent, const rapidxml::xml_node< char > *xml_node, const std::set< std::string > &varsRetain={})
Definition: xml_utils.cpp:498
World.h
mvsim::World::evaluate_tag_if
bool evaluate_tag_if(const rapidxml::xml_node< char > &node) const
Definition: World_load_xml.cpp:357
xml_utils.h
findClosing
static std::string::size_type findClosing(size_t pos, const std::string &s, const char searchEndChar, const char otherStartChar)
Definition: xml_utils.cpp:341
mvsim::parse_variables
std::string parse_variables(const std::string &in, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain)
Definition: xml_utils.cpp:428
void
typedef void(GLAD_API_PTR *GLDEBUGPROC)(GLenum source
mvsim::World::local_to_abs_path
std::string local_to_abs_path(const std::string &in_path) const
Definition: World.cpp:106
splitVerticalBar
static std::tuple< std::string, std::string > splitVerticalBar(const std::string &s)
Definition: xml_utils.cpp:365
rapidxml
Definition: rapidxml.hpp:57
rapidxml::xml_node::first_node
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
parseVars
static std::string parseVars(const std::string &text, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain, const size_t searchStartPos=0)
Definition: xml_utils.cpp:373
mvsim::TParameterDefinitions
std::map< std::string, TParamEntry > TParameterDefinitions
Definition: TParameterDefinitions.h:64
rapidxml_utils.hpp
mvsim::parse
std::string parse(const std::string &input, const std::map< std::string, std::string > &variableNamesValues={})
Definition: parse_utils.cpp:217
mvsim::trim
std::string trim(const std::string &s)
Definition: parse_utils.cpp:251
mvsim::readXmlAndGetRoot
std::tuple< XML_Doc_Data::Ptr, rapidxml::xml_node<> * > readXmlAndGetRoot(const std::string &pathToFile, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain={})
Definition: xml_utils.cpp:315
rapidxml::parse_error::where
Ch * where() const
Definition: rapidxml.hpp:94
mvsim::World
Definition: World.h:82
start
ROSCPP_DECL void start()
sajson::parse
document parse(const AllocationStrategy &strategy, const StringType &string)
Definition: sajson.h:2479
rapidxml::xml_node< char >
mvsim::World::user_defined_variables
const std::map< std::string, std::string > & user_defined_variables() const
Definition: World.h:390
recursive_xml_to_str_solving_includes
static void recursive_xml_to_str_solving_includes(const World &parent, const rapidxml::xml_node< char > *n, const std::set< std::string > &varsRetain, std::stringstream &ss)
Definition: xml_utils.cpp:447
mvsim::parseXYPHI
mrpt::math::TPose2D parseXYPHI(const std::string &s, bool allow_missing_angle=false, double default_angle_radians=0.0, const std::map< std::string, std::string > &variableNamesValues={})
Definition: xml_utils.cpp:235
mvsim::parse_xmlnode_shape
void parse_xmlnode_shape(const rapidxml::xml_node< char > &xml_node, mrpt::math::TPolygon2D &out_poly, const char *functionNameContext="")
Definition: xml_utils.cpp:258
mvsim::parse_xmlnode_as_param
bool parse_xmlnode_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:196
param
T param(const std::string &param_name, const T &default_val)
rapidxml::xml_attribute::next_attribute
xml_attribute< Ch > * next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:856
root
root
rapidxml::parse_error::what
virtual const char * what() const
Definition: rapidxml.hpp:85
mvsim::parse_xmlnode_attribs
void parse_xmlnode_attribs(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:182
rapidxml::xml_node::first_attribute
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
mvsim::readXmlTextAndGetRoot
std::tuple< std::shared_ptr< rapidxml::xml_document<> >, rapidxml::xml_node<> * > readXmlTextAndGetRoot(const std::string &xmlData, const std::string &pathToFile)
Definition: xml_utils.cpp:290
rapidxml::parse_error
Definition: rapidxml.hpp:71
rapidxml_print.hpp
rapidxml::xml_node::next_sibling
xml_node< Ch > * next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1004


mvsim
Author(s):
autogenerated on Wed May 28 2025 02:13:08