Refactor numerical parsing in XML.

PiperOrigin-RevId: 653979240
Change-Id: I4d39f4041b88396193eb35f0cc393141d64f6fc0
This commit is contained in:
Kyle Bayes
2024-07-19 05:40:54 -07:00
committed by Copybara-Service
parent e92af73cbf
commit 8a8a44a4db
13 changed files with 378 additions and 314 deletions
+47 -138
View File
@@ -43,10 +43,6 @@
namespace {
using std::size_t;
using std::string;
using std::stringstream;
using std::vector;
using tinyxml2::XMLAttribute;
using tinyxml2::XMLElement;
@@ -238,7 +234,7 @@ mjXSchema::mjXSchema(const char* schema[][mjXATTRNUM], unsigned nrow) {
// get pointer to error message
string mjXSchema::GetError() {
std::string mjXSchema::GetError() {
return error;
}
@@ -256,7 +252,7 @@ static void printspace(std::stringstream& str, int n, const char* space) {
// print schema as text
void mjXSchema::Print(std::stringstream& str, int level) const {
// replace body with (world)body
string name1 = (name_ == "body") ? "(world)body" : name_;
std::string name1 = (name_ == "body") ? "(world)body" : name_;
// space, name, type
printspace(str, 3*level, " ");
@@ -290,7 +286,7 @@ void mjXSchema::Print(std::stringstream& str, int level) const {
// print schema as HTML table
void mjXSchema::PrintHTML(std::stringstream& str, int level, bool pad) const {
// replace body with (world)body
string name1 = (name_ == "body" ? "(world)body" : name_);
std::string name1 = (name_ == "body" ? "(world)body" : name_);
// open table
if (level==0) {
@@ -377,7 +373,7 @@ XMLElement* mjXSchema::Check(XMLElement* elem, int level) {
const XMLAttribute* attribute = elem->FirstAttribute();
for (; attribute != nullptr; attribute = attribute->Next()) {
if (attr_.find(attribute->Name()) == attr_.end()) {
error = "unrecognized attribute: '" + string(attribute->Name()) + "'";
error = "unrecognized attribute: '" + std::string(attribute->Name()) + "'";
return elem;
}
}
@@ -541,7 +537,7 @@ template bool mjXUtil::SameVector(const unsigned char* vec1, const unsigned char
// find string in map, return corresponding integer (-1: not found)
int mjXUtil::FindKey(const mjMap* map, int mapsz, string key) {
int mjXUtil::FindKey(const mjMap* map, int mapsz, std::string key) {
for (int i=0; i<mapsz; i++) {
if (map[i].key == key) {
return map[i].value;
@@ -554,7 +550,7 @@ int mjXUtil::FindKey(const mjMap* map, int mapsz, string key) {
// find integer in map, return corresponding string ("": not found)
string mjXUtil::FindValue(const mjMap* map, int mapsz, int value) {
std::string mjXUtil::FindValue(const mjMap* map, int mapsz, int value) {
for (int i=0; i<mapsz; i++) {
if (map[i].value == value) {
return map[i].key;
@@ -570,9 +566,22 @@ string mjXUtil::FindValue(const mjMap* map, int mapsz, int value) {
template<typename T>
std::optional<std::vector<T>> mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr,
bool required) {
std::vector<T> vec;
ReadAttrValues<T>(elem, attr, [&](int i, T num) { vec.push_back(num); });
if (!vec.size()) {
std::vector<T> v;
const char* raw_cstr = elem->Attribute(attr);
if (raw_cstr) {
v = mujoco::user::StringToVector<T>(raw_cstr);
if (errno == EDOM) {
mju_warning("XML contains a 'NaN'. Please check it carefully.");
} else if (errno == ERANGE) {
throw mjXError(elem, "number is too large in attribute '%s'", attr);
} else if (errno == EINVAL) {
throw mjXError(elem, "bad format in attribute '%s'", attr);
} else if (errno != 0) {
throw mjXError(elem, "unknown error in attribute '%s'", attr);
}
}
if (!v.size()) {
if (required) {
throw mjXError(elem, "required attribute missing: '%s'", attr);
} else {
@@ -580,7 +589,7 @@ std::optional<std::vector<T>> mjXUtil::ReadAttrVec(XMLElement* elem, const char*
}
}
return vec;
return v;
}
template std::optional<std::vector<double>>
@@ -651,7 +660,7 @@ mjXUtil::ReadAttrNum(XMLElement* elem, const char* attr, bool required);
// return number of elements found
template<typename T>
int mjXUtil::ReadAttr(XMLElement* elem, const char* attr, const int len,
T* data, string& text, bool required, bool exact) {
T* data, std::string& text, bool required, bool exact) {
auto maybe_vec = ReadAttrVec<T>(elem, attr, required);
if (!maybe_vec.has_value()) {
return 0;
@@ -672,21 +681,21 @@ int mjXUtil::ReadAttr(XMLElement* elem, const char* attr, const int len,
}
template int mjXUtil::ReadAttr(XMLElement* elem, const char* attr, int len,
double* data, string& text, bool required, bool exact);
double* data, std::string& text, bool required, bool exact);
template int mjXUtil::ReadAttr(XMLElement* elem, const char* attr, int len,
float* data, string& text, bool required, bool exact);
float* data, std::string& text, bool required, bool exact);
template int mjXUtil::ReadAttr(XMLElement* elem, const char* attr, int len,
int* data, string& text, bool required, bool exact);
int* data, std::string& text, bool required, bool exact);
template int mjXUtil::ReadAttr(XMLElement* elem, const char* attr, int len,
unsigned char* data, string& text, bool required,
unsigned char* data, std::string& text, bool required,
bool exact);
// read quaternion attribute
// throw error if identically zero
int mjXUtil::ReadQuat(XMLElement* elem, const char* attr, double* data, string& text,
int mjXUtil::ReadQuat(XMLElement* elem, const char* attr, double* data, std::string& text,
bool required) {
ReadAttr(elem, attr, /*len=*/4, data, text, required, /*exact=*/true);
@@ -700,7 +709,7 @@ int mjXUtil::ReadQuat(XMLElement* elem, const char* attr, double* data, string&
// read DOUBLE array into C++ vector, return number read
int mjXUtil::ReadVector(XMLElement* elem, const char* attr,
vector<double>& vec, string& text, bool required) {
std::vector<double>& vec, std::string& text, bool required) {
auto maybe_vec = ReadAttrVec<double>(elem, attr, required);
if (!maybe_vec.has_value()) {
return 0;
@@ -713,7 +722,8 @@ int mjXUtil::ReadVector(XMLElement* elem, const char* attr,
// read text field
bool mjXUtil::ReadAttrTxt(tinyxml2::XMLElement* elem, const char* attr, string& text, bool required) {
bool mjXUtil::ReadAttrTxt(tinyxml2::XMLElement* elem, const char* attr,
std::string& text, bool required) {
auto maybe_str = ReadAttrStr(elem, attr, required);
if (!maybe_str.has_value()) {
return false;
@@ -735,92 +745,9 @@ bool mjXUtil::ReadAttrInt(XMLElement* elem, const char* attr, int* data, bool re
}
template<> int mjXUtil::StrToNum(char* str, char** c) {
return std::strtol(str, c, 10);
}
template<> float mjXUtil::StrToNum(char* str, char** c) {
return std::strtof(str, c);
}
template<> double mjXUtil::StrToNum(char* str, char** c) {
return std::strtod(str, c);
}
template <typename T>
std::vector<T> mjXUtil::String2Vector(const std::string& s) {
errno = 0;
std::vector<T> v;
char* cs = (char*) s.c_str();
char* ch = cs;
// reserve worst case
v.reserve((s.size() >> 1) + 1);
for (;;) {
cs = ch;
T num = StrToNum<T>(cs, &ch);
if (cs == ch) break;
if (errno) break;
v.push_back(num);
}
v.shrink_to_fit();
return v;
}
template std::vector<int> mjXUtil::String2Vector(const std::string& s);
template std::vector<float> mjXUtil::String2Vector(const std::string& s);
template std::vector<double> mjXUtil::String2Vector(const std::string& s);
template<>
std::vector<std::string> mjXUtil::String2Vector(const std::string& s) {
std::vector<std::string> v;
std::stringstream ss(s);
std::string word;
while (ss >> word) {
v.push_back(word);
}
return v;
}
// write vector<float> to string
void mjXUtil::Vector2String(string& txt, const vector<string>& vec) {
stringstream strm;
for (size_t i=0; i<vec.size(); i++) {
if (i>0) {
strm << " ";
}
strm << vec[i];
}
txt = strm.str();
}
// write vector<double> to string
void mjXUtil::Vector2String(string& txt, const vector<double>& vec) {
stringstream strm;
for (size_t i=0; i<vec.size(); i++) {
if (i>0) {
strm << " ";
}
strm << vec[i];
}
txt = strm.str();
}
// write vector<float> to string
void mjXUtil::Vector2String(string& txt, const vector<float>& vec, int ncol) {
stringstream strm;
void mjXUtil::Vector2String(std::string& txt, const std::vector<float>& vec, int ncol) {
std::stringstream strm;
for (size_t i=0; i<vec.size(); i++) {
if (ncol && (i % ncol) == 0) {
@@ -834,26 +761,8 @@ void mjXUtil::Vector2String(string& txt, const vector<float>& vec, int ncol) {
txt = strm.str();
}
// write vector<int> to string
void mjXUtil::Vector2String(string& txt, const vector<int>& vec) {
stringstream strm;
for (size_t i=0; i<vec.size(); i++) {
if (i>0) {
strm << " ";
}
strm << vec[i];
}
txt = strm.str();
}
// find subelement with given name, make sure it is unique
XMLElement* mjXUtil::FindSubElem(XMLElement* elem, string name, bool required) {
XMLElement* mjXUtil::FindSubElem(XMLElement* elem, std::string name, bool required) {
XMLElement* subelem = 0;
XMLElement* iter = elem->FirstChildElement();
@@ -924,7 +833,7 @@ static int Round(double x) {
// write attribute
template<typename T>
void mjXUtil::WriteAttr(XMLElement* elem, string name, int n, const T* data, const T* def,
void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n, const T* data, const T* def,
bool trim) {
// make sure all are defined
if constexpr (std::is_floating_point_v<T>) {
@@ -948,7 +857,7 @@ void mjXUtil::WriteAttr(XMLElement* elem, string name, int n, const T* data, con
}
// increase precision for testing
stringstream stream;
std::stringstream stream;
stream.precision(mujoco::_mjPRIVATE__get_xml_precision());
// process all numbers
@@ -972,22 +881,22 @@ void mjXUtil::WriteAttr(XMLElement* elem, string name, int n, const T* data, con
}
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
template void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n,
const double* data, const double* def, bool trim);
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
template void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n,
const float* data, const float* def, bool trim);
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
template void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n,
const int* data, const int* def, bool trim);
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
template void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n,
const unsigned char* data,
const unsigned char* def, bool trim);
// write vector<double> attribute, default = zero array
void mjXUtil::WriteVector(XMLElement* elem, string name, const vector<double>& vec) {
void mjXUtil::WriteVector(XMLElement* elem, std::string name, const std::vector<double>& vec) {
// proceed only if non-zero found
bool ok = false;
for (size_t i=0; i<vec.size(); i++) {
@@ -1006,8 +915,8 @@ void mjXUtil::WriteVector(XMLElement* elem, string name, const vector<double>& v
// write vector<double> attribute, default with same size
void mjXUtil::WriteVector(XMLElement* elem, string name, const vector<double>& vec,
const vector<double>& def) {
void mjXUtil::WriteVector(XMLElement* elem, std::string name, const std::vector<double>& vec,
const std::vector<double>& def) {
// proceed only if non-zero found
bool ok = false;
for (size_t i=0; i<vec.size(); i++) {
@@ -1026,7 +935,7 @@ void mjXUtil::WriteVector(XMLElement* elem, string name, const vector<double>& v
// write attribute- string
void mjXUtil::WriteAttrTxt(XMLElement* elem, string name, string value) {
void mjXUtil::WriteAttrTxt(XMLElement* elem, std::string name, std::string value) {
// skip if value is empty
if (value.empty()) {
return;
@@ -1039,7 +948,7 @@ void mjXUtil::WriteAttrTxt(XMLElement* elem, string name, string value) {
// write attribute- single int
void mjXUtil::WriteAttrInt(XMLElement* elem, string name, int data, int def) {
void mjXUtil::WriteAttrInt(XMLElement* elem, std::string name, int data, int def) {
// skip default
if (data==def) {
return;
@@ -1051,7 +960,7 @@ void mjXUtil::WriteAttrInt(XMLElement* elem, string name, int data, int def) {
// write attribute- keyword
void mjXUtil::WriteAttrKey(XMLElement* elem, string name,
void mjXUtil::WriteAttrKey(XMLElement* elem, std::string name,
const mjMap* map, int mapsz, int data, int def) {
// skip default
if (data==def) {