Newer
Older
Martin Christoph Hierholzer
committed
#include <libxml++/libxml++.h>
#include "ConfigReader.h"
namespace ChimeraTK {
/*********************************************************************************************************************/
/** Functor to fill variableMap */
struct FunctorFill {
FunctorFill(ConfigReader *owner, const std::string &type, const std::string &name, const std::string &value,
bool &processed)
Martin Christoph Hierholzer
committed
: _owner(owner), _type(type), _name(name), _value(value), _processed(processed)
{}
Martin Christoph Hierholzer
committed
template<typename PAIR>
void operator()(PAIR&) const {
Martin Christoph Hierholzer
committed
// extract the user type from the pair
typedef typename PAIR::first_type T;
Martin Christoph Hierholzer
committed
// skip this type, if not matching the type string in the config file
if(_type != boost::fusion::at_key<T>(_owner->typeMap)) return;
Martin Christoph Hierholzer
committed
_owner->createVar<T>(_name, _value);
_processed = true;
}
Martin Christoph Hierholzer
committed
ConfigReader *_owner;
const std::string &_type, &_name, &_value;
bool &_processed; // must be a non-const reference, since we want to return this to the caller
typedef boost::fusion::pair<std::string,ConfigReader::Var<std::string>> StringPair;
};
/*********************************************************************************************************************/
/** Functor to fill variableMap for arrays */
struct ArrayFunctorFill {
ArrayFunctorFill(ConfigReader *owner, const std::string &type, const std::string &name,
const std::map<size_t, std::string> &values, bool &processed)
: _owner(owner), _type(type), _name(name), _values(values), _processed(processed)
{}
template<typename PAIR>
void operator()(PAIR&) const {
// extract the user type from the pair
typedef typename PAIR::first_type T;
// skip this type, if not matching the type string in the config file
if(_type != boost::fusion::at_key<T>(_owner->typeMap)) return;
_owner->createArray<T>(_name, _values);
_processed = true;
}
ConfigReader *_owner;
const std::string &_type, &_name;
const std::map<size_t, std::string> &_values;
bool &_processed; // must be a non-const reference, since we want to return this to the caller
Martin Christoph Hierholzer
committed
typedef boost::fusion::pair<std::string,ConfigReader::Var<std::string>> StringPair;
};
/*********************************************************************************************************************/
template<typename T>
void ConfigReader::createVar(const std::string &name, const std::string &value) {
// convert value into user type
/// @todo error handling!
std::stringstream stream(value);
T convertedValue;
if(typeid(T) == typeid(int8_t) || typeid(T) == typeid(uint8_t)) { // prevent interpreting int8-types as characters
int16_t intermediate;
stream >> intermediate;
convertedValue = intermediate;
}
else { // note: string is done in template specialisation
stream >> convertedValue;
}
Martin Christoph Hierholzer
committed
// place the variable onto the vector
Martin Christoph Hierholzer
committed
std::map<std::string, ConfigReader::Var<T>> &theMap = boost::fusion::at_key<T>(variableMap.table);
theMap.emplace(std::make_pair(name, ConfigReader::Var<T>(this, name, convertedValue)));
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
template<>
void ConfigReader::createVar<std::string>(const std::string &name, const std::string &value) {
// place the variable onto the vector
Martin Christoph Hierholzer
committed
std::map<std::string, ConfigReader::Var<std::string>> &theMap = boost::fusion::at_key<std::string>(variableMap.table);
theMap.emplace(std::make_pair(name, ConfigReader::Var<std::string>(this, name, value)));
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*********************************************************************************************************************/
template<typename T>
void ConfigReader::createArray(const std::string &name, const std::map<size_t, std::string> &values) {
std::vector<T> Tvalues;
size_t expectedIndex = 0;
for(auto it = values.begin(); it != values.end(); ++it) {
// check index (std::map should be ordered by the index)
if(it->first != expectedIndex) {
parsingError("Array index "+std::to_string(expectedIndex)+" not found, but "+std::to_string(it->first)+" was. "
"Sparse arrays are not supported!");
}
++expectedIndex;
// convert value into user type
std::stringstream stream(it->second);
T convertedValue;
if(typeid(T) == typeid(int8_t) || typeid(T) == typeid(uint8_t)) { // prevent interpreting int8-types as characters
int16_t intermediate;
stream >> intermediate;
convertedValue = intermediate;
}
else { // note: string is done in template specialisation
stream >> convertedValue;
}
// store value in vector
Tvalues.push_back(convertedValue);
}
// place the variable onto the vector
std::map<std::string, ConfigReader::Array<T>> &theMap = boost::fusion::at_key<T>(arrayMap.table);
theMap.emplace(std::make_pair(name, ConfigReader::Array<T>(this, name, Tvalues)));
}
/*********************************************************************************************************************/
template<>
void ConfigReader::createArray<std::string>(const std::string &name, const std::map<size_t, std::string> &values) {
std::vector<std::string> Tvalues;
size_t expectedIndex = 0;
for(auto it = values.begin(); it != values.end(); ++it) {
// check index (std::map should be ordered by the index)
if(it->first != expectedIndex) {
parsingError("Array index "+std::to_string(expectedIndex)+" not found, but "+std::to_string(it->first)+" was. "
"Sparse arrays are not supported!");
}
++expectedIndex;
// store value in vector
Tvalues.push_back(it->second);
}
// place the variable onto the vector
std::map<std::string, ConfigReader::Array<std::string>> &theMap = boost::fusion::at_key<std::string>(arrayMap.table);
theMap.emplace(std::make_pair(name, ConfigReader::Array<std::string>(this, name, Tvalues)));
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
ConfigReader::ConfigReader(EntityOwner *owner, const std::string &name, const std::string &fileName,
const std::unordered_set<std::string> &tags)
: ApplicationModule(owner, name, "Configuration read from file '"+fileName+"'", false, tags),
_fileName(fileName)
{
// parse the file into a DOM structure
xmlpp::DomParser parser;
try {
parser.parse_file(fileName);
}
catch(xmlpp::exception &e) { /// @todo change exception!
throw std::runtime_error("ConfigReader: Error opening the config file '"+fileName+"': "+e.what());
}
Martin Christoph Hierholzer
committed
// get root element
const auto root = parser.get_document()->get_root_node();
if(root->get_name() != "configuration") {
parsingError("Expected 'configuration' tag instead of: "+root->get_name());
}
// parsing loop
for(const auto& child : root->get_children()) {
// cast into element, ignore if not an element (e.g. comment)
const xmlpp::Element *element = dynamic_cast<const xmlpp::Element*>(child);
if(!element) continue;
if(element->get_name() != "variable") {
parsingError("Expected 'variable' tag instead of: "+root->get_name());
}
Martin Christoph Hierholzer
committed
// obtain attributes from the element
Martin Christoph Hierholzer
committed
auto name = element->get_attribute("name");
if(!name) parsingError("Missing attribute 'name' for the 'variable' tag.");
auto type = element->get_attribute("type");
if(!type) parsingError("Missing attribute 'type' for the 'variable' tag.");
// scalar value: obtain value from attribute
Martin Christoph Hierholzer
committed
auto value = element->get_attribute("value");
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
if(value) {
// create accessor and store value in map using the functor
bool processed{false};
boost::fusion::for_each( variableMap.table,
FunctorFill(this, type->get_value(), name->get_value(), value->get_value(), processed) );
if(!processed) parsingError("Incorrect value '"+type->get_value()+"' for attribute 'type' of the 'variable' tag.");
}
// array value: obtain values from child elements
else {
bool valueFound = false;
std::map<size_t, std::string> values;
for(const auto& valueChild : child->get_children()) {
// obtain value child element and extract index and value from attributes
const xmlpp::Element *valueElement = dynamic_cast<const xmlpp::Element*>(valueChild);
if(!valueElement) continue; // ignore comments etc.
if(valueElement->get_name() != "value") {
parsingError("Expected 'value' tag instead of: "+root->get_name());
}
valueFound = true;
auto index = valueElement->get_attribute("i");
if(!index) parsingError("Missing attribute 'index' for the 'value' tag.");
auto value = valueElement->get_attribute("v");
if(!value) parsingError("Missing attribute 'value' for the 'value' tag.");
// get index as number and store value as a string
size_t intIndex;
try {
intIndex = std::stoi(index->get_value());
}
catch(std::exception &e) {
parsingError("Cannot parse string '"+std::string(index->get_value())+"' as an index number: "+e.what());
}
values[intIndex] = value->get_value();
}
// make sure there is at least one value
if(!valueFound) {
parsingError("Each variable must have a value, either specified as an attribute or as child tags.");
}
// create accessor and store array value in map using functor
bool processed{false};
boost::fusion::for_each( variableMap.table,
ArrayFunctorFill(this, type->get_value(), name->get_value(), values, processed) );
if(!processed) parsingError("Incorrect value '"+type->get_value()+"' for attribute 'type' of the 'variable' tag.");
}
Martin Christoph Hierholzer
committed
}
}
/********************************************************************************************************************/
void ConfigReader::parsingError(const std::string &message) {
throw std::runtime_error("ConfigReader: Error parsing the config file '"+_fileName+"': "+message);
}
/*********************************************************************************************************************/
/** Functor to set values to the scalar accessors */
Martin Christoph Hierholzer
committed
struct FunctorSetValues {
FunctorSetValues(ConfigReader *owner) : _owner(owner) {}
Martin Christoph Hierholzer
committed
template<typename PAIR>
void operator()(PAIR&) const {
Martin Christoph Hierholzer
committed
// get user type and vector
typedef typename PAIR::first_type T;
Martin Christoph Hierholzer
committed
std::map<std::string, ConfigReader::Var<T>> &theMap = boost::fusion::at_key<T>(_owner->variableMap.table);
Martin Christoph Hierholzer
committed
// iterate vector and set values
Martin Christoph Hierholzer
committed
for(auto &pair : theMap) {
auto &var = pair.second;
Martin Christoph Hierholzer
committed
var._accessor = var._value;
var._accessor.write();
}
Martin Christoph Hierholzer
committed
}
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
ConfigReader *_owner;
};
/*********************************************************************************************************************/
/** Functor to set values to the array accessors */
struct FunctorSetValuesArray {
FunctorSetValuesArray(ConfigReader *owner) : _owner(owner) {}
template<typename PAIR>
void operator()(PAIR&) const {
// get user type and vector
typedef typename PAIR::first_type T;
std::map<std::string, ConfigReader::Array<T>> &theMap = boost::fusion::at_key<T>(_owner->arrayMap.table);
// iterate vector and set values
for(auto &pair : theMap) {
auto &var = pair.second;
var._accessor = var._value;
var._accessor.write();
}
}
Martin Christoph Hierholzer
committed
ConfigReader *_owner;
};
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void ConfigReader::prepare() {
Martin Christoph Hierholzer
committed
boost::fusion::for_each( variableMap.table, FunctorSetValues(this) );
boost::fusion::for_each( arrayMap.table, FunctorSetValuesArray(this) );
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
} // namespace ChimeraTK