ChimeraTK-DeviceAccess  03.18.00
LogicalNameMapParser.cc
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 
4 #include "LogicalNameMapParser.h"
5 
6 #include "DeviceBackend.h"
7 #include "Exception.h"
9 #include <libxml++/libxml++.h>
10 
11 #include <stdexcept>
12 
13 namespace ChimeraTK {
14 
15  template<>
16  std::string LogicalNameMapParser::getValueFromXmlSubnode<std::string>(const xmlpp::Node* node,
17  const std::string& subnodeName, BackendRegisterCatalogue<LNMBackendRegisterInfo> const& catalogue,
18  bool hasDefault, std::string defaultValue) {
19  auto list = node->find(subnodeName);
20  if(list.empty() && hasDefault) return defaultValue;
21  if(list.size() != 1) {
22  parsingError(node,
23  "Expected exactly one subnode of the type '" + subnodeName + "' below node '" + node->get_name() + "'.");
24  }
25  auto childList = list[0]->get_children();
26 
27  std::string value;
28  for(auto& child : childList) {
29  if(!child) {
30  parsingError(child, "Got nullptr from parser library.");
31  }
32 
33  // Check for CDATA node
34  const auto* cdataNode = dynamic_cast<const xmlpp::CdataNode*>(child);
35  if(cdataNode) {
36  value += cdataNode->get_content();
37  continue;
38  }
39 
40  // check for plain text
41  const auto* textNode = dynamic_cast<const xmlpp::TextNode*>(child);
42  if(textNode) {
43  // put to stream buffer
44  value += textNode->get_content();
45  continue;
46  }
47 
48  // check for reference
49  if(child->get_name() == "ref") {
50  auto childChildList = child->get_children();
51  const auto* refNameNode = dynamic_cast<const xmlpp::TextNode*>(childChildList.front());
52  if(refNameNode && childChildList.size() == 1) {
53  std::string regName = refNameNode->get_content();
54  if(!catalogue.hasRegister(regName)) {
55  parsingError(child, "Reference to constant '" + regName + "' could not be resolved.");
56  }
57  auto reg = catalogue.getBackendRegister(regName);
58  // auto reg_casted = boost::dynamic_pointer_cast<LNMBackendRegisterInfo>(reg);
59  // assert(reg_casted != nullptr); // this is our own catalogue
60  // fetch the value of the target constant
61  if(reg.targetType == LNMBackendRegisterInfo::TargetType::CONSTANT) {
62  if(!reg.plugins.empty()) {
63  parsingError(childList.front(), "'" + regName + "' uses plugins which is not supported for <ref>");
64  }
65  // put to stream buffer
66  auto& lnmVariable = _variables[reg.name];
67  callForType(reg.valueType, [&](auto arg) {
68  std::stringstream buf;
69  buf << boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue[0];
70  value = buf.str();
71  });
72  continue;
73  }
74  parsingError(child, "Reference to '" + regName + "' does not refer to a constant.");
75  }
76  else {
77  parsingError(child, "The <ref> node must contain only text.");
78  }
79  }
80 
81  // check for parameter
82  if(child->get_name() == "par") {
83  auto childChildList = child->get_children();
84  const auto* parNameNode = dynamic_cast<const xmlpp::TextNode*>(childChildList.front());
85  if(parNameNode && childChildList.size() == 1) {
86  std::string parName = parNameNode->get_content();
87  if(_parameters.find(parName) == _parameters.end()) {
88  parsingError(child, "Parameter '" + parName + "' could not be resolved.");
89  }
90  // put to stream buffer
91  value += _parameters[parName];
92  continue;
93  }
94  parsingError(child, "The <par> node must contain only text.");
95  }
96 
97  // neither found: throw error
98  parsingError(node,
99  "Node '" + subnodeName +
100  "' should contain only text, CDATA sections, references or parameters. Instead child '" +
101  child->get_name() + "' was found.");
102  }
103  return value;
104  } // namespace ChimeraTK
105 
106  /********************************************************************************************************************/
107 
108  template<typename T>
109  static T stringToValue(const std::string& valAsString) {
110  if constexpr(std::is_integral_v<T>) {
111  // special case of integers: handle prefix for hex or octal notation, like 0xff, 077
112  T value;
113  try {
114  // base=0 means auto detect base, depending on prefix
115  int64_t longVal = std::stoll(valAsString, nullptr, 0);
116  if(std::is_unsigned_v<T>) {
117  if(longVal < 0) {
118  throw std::out_of_range(std::string("negative!"));
119  }
120  }
121  else {
122  if(longVal < (int64_t)std::numeric_limits<T>::min()) {
123  throw std::out_of_range(std::string("too small!"));
124  }
125  }
126  // we need to exclude upper range check for uint64 since max val does not fit into longVal
127  if(!std::is_same_v<T, uint64_t> && longVal > (int64_t)std::numeric_limits<T>::max()) {
128  throw std::out_of_range(std::string("too large!"));
129  }
130  value = longVal;
131  }
132  catch(std::exception& e) {
133  std::string msg =
134  "LogicalNameMapParser: string '" + valAsString + "' cannot be converted to integer (" + e.what() + ")";
135  throw ChimeraTK::logic_error(msg);
136  }
137  return value;
138  }
139  else {
140  // generic case: put into stream
141  std::stringstream stream;
142  stream << valAsString;
143  // interpret stream as value of type T and return it
144  T value;
145  stream >> value;
146  return value;
147  }
148  }
149 
150  template<typename T>
151  T LogicalNameMapParser::getValueFromXmlSubnode(const xmlpp::Node* node, const std::string& subnodeName,
152  BackendRegisterCatalogue<LNMBackendRegisterInfo> const& catalogue, bool hasDefault, T defaultValue) {
153  // obtain result as string
154  auto valAsString =
155  getValueFromXmlSubnode<std::string>(node, subnodeName, catalogue, hasDefault, std::to_string(defaultValue));
156 
157  return stringToValue<T>(valAsString);
158  }
159 
160  /********************************************************************************************************************/
161 
162  template<typename T>
163  std::vector<T> LogicalNameMapParser::getValueVectorFromXmlSubnode(const xmlpp::Node* node,
164  const std::string& subnodeName, BackendRegisterCatalogue<LNMBackendRegisterInfo> const& catalogue) {
165  auto list = node->find(subnodeName);
166  if(list.empty()) {
167  parsingError(node,
168  "Expected at least one subnode of the type '" + subnodeName + "' below node '" + node->get_name() + "'.");
169  }
170 
171  std::vector<T> valueVector;
172 
173  for(auto& child : list) {
174  const auto* childElement = dynamic_cast<const xmlpp::Element*>(child);
175  if(!childElement) continue; // ignore comments etc.
176 
177  // obtain index and resize valueVector if necessary
178  auto* indexAttr = childElement->get_attribute("index");
179  size_t index = 0;
180  if(indexAttr) {
181  index = std::stoi(indexAttr->get_value());
182  }
183  if(index >= valueVector.size()) valueVector.resize(index + 1);
184 
185  // get content
186  auto childList = childElement->get_children();
187  if(childList.size() != 1) {
188  parsingError(childElement,
189  "Node '" + subnodeName +
190  "' should contain only text or exactly one reference, "
191  "instead multiple childs "
192  "were found.");
193  }
194 
195  // check for plain text
196  const auto* textNode = dynamic_cast<const xmlpp::TextNode*>(childList.front());
197  if(textNode) {
198  std::string valAsString = textNode->get_content();
199  valueVector[index] = stringToValue<T>(valAsString);
200  continue;
201  }
202 
203  // check for reference
204  if(childList.front()->get_name() == "ref") {
205  auto childChildList = childList.front()->get_children();
206  const auto* refNameNode = dynamic_cast<const xmlpp::TextNode*>(childChildList.front());
207  if(refNameNode && childChildList.size() == 1) {
208  std::string regName = refNameNode->get_content();
209  if(!catalogue.hasRegister(regName)) {
210  parsingError(childList.front(), "Reference to constant '" + regName + "' could not be resolved.");
211  }
212  auto reg = catalogue.getBackendRegister(regName);
213  // fetch the value of the target constant
214  if(reg.targetType == LNMBackendRegisterInfo::TargetType::CONSTANT) {
215  if(!reg.plugins.empty()) {
216  parsingError(childList.front(), "'" + regName + "' uses plugins which is not supported for <ref>");
217  }
218  // convert via string
219  std::stringstream buf;
220  auto& lnmVariable = _variables[reg.name];
221  callForType(reg.valueType, [&](auto arg) {
222  buf << boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue[0];
223  });
224  std::string strVal;
225  buf >> strVal;
226  valueVector[index] = stringToValue<T>(strVal);
227  continue;
228  }
229  parsingError(childList.front(), "Reference to '" + regName + "' does not refer to a constant.");
230  }
231  else {
232  parsingError(childList.front(), "The <ref> node must contain only text.");
233  }
234  }
235 
236  if(childList.front()->get_name() == "par") {
237  auto childChildList = childList.front()->get_children();
238  const auto* parNameNode = dynamic_cast<const xmlpp::TextNode*>(childChildList.front());
239  if(parNameNode && childChildList.size() == 1) {
240  std::string parName = parNameNode->get_content();
241  if(_parameters.find(parName) == _parameters.end()) {
242  parsingError(childList.front(), "Parameter '" + parName + "' could not be resolved.");
243  }
244  valueVector[index] = stringToValue<T>(_parameters[parName]);
245  continue;
246  }
247  parsingError(childList.front(), "The <par> node must contain only text.");
248  }
249 
250  // neither found: throw error
251  parsingError(node,
252  "Node '" + subnodeName + "' should contain only text or exactly one reference, instead child '" +
253  childList.front()->get_name() + "' was found.");
254  }
255 
256  return valueVector;
257  }
258 
259  /********************************************************************************************************************/
260 
262  _fileName = fileName;
263 
265 
266  // parse the file into a DOM structure
267  xmlpp::DomParser parser;
268  try {
269  parser.parse_file(fileName);
270  }
271  catch(xmlpp::exception& e) {
272  throw ChimeraTK::logic_error("Error opening the xlmap file '" + fileName + "': " + e.what());
273  }
274 
275  // get root element
276  auto* const root = parser.get_document()->get_root_node();
277  if(root->get_name() != "logicalNameMap") {
278  parsingError(root, "Expected 'logicalNameMap' tag instead of: " + root->get_name());
279  }
280 
281  // parsing loop
282  for(const auto& child : root->get_children()) {
283  // cast into element, ignore if not an element (e.g. comment)
284  const auto* element = dynamic_cast<const xmlpp::Element*>(child);
285  if(!element) continue;
286 
287  // parse the element
288  parseElement(RegisterPath(), element, catalogue);
289  }
290 
291  return catalogue;
292  }
293 
294  void LogicalNameMapParser::parseElement(const RegisterPath& currentPath, const xmlpp::Element* element,
296  // module tag found: look for registers and sub-modules in module
297  if(element->get_name() == "module") {
298  // obtain name of the module
299  auto* nameAttr = element->get_attribute("name");
300  if(!nameAttr) {
301  parsingError(element, "Missing name attribute of 'module' tag.");
302  }
303  std::string moduleName = nameAttr->get_value();
304 
305  // iterate over children in module
306  for(const auto& child : element->get_children()) {
307  // cast into element, ignore if not an element (e.g. comment)
308  const auto* childElement = dynamic_cast<const xmlpp::Element*>(child);
309  if(!childElement) continue;
310 
311  // parse the element
312  parseElement(currentPath / moduleName, childElement, catalogue);
313  }
314  }
315 
316  // register tag found: create new entry in map
317  else {
318  // obtain the type
319  std::string type = element->get_name();
320 
321  // obtain name of logical register
322  auto* nameAttr = element->get_attribute("name");
323  if(!nameAttr) {
324  parsingError(element, "Missing name attribute of '" + type + "' tag.");
325  }
326  RegisterPath registerName = currentPath / std::string(nameAttr->get_value());
327 
328  // create new RegisterInfo object
330  info.name = registerName;
331  if(type == "redirectedRegister") {
332  info.targetType = LNMBackendRegisterInfo::TargetType::REGISTER;
333  info.deviceName = getValueFromXmlSubnode<std::string>(element, "targetDevice", catalogue);
334  info.registerName = getValueFromXmlSubnode<std::string>(element, "targetRegister", catalogue);
335  info.firstIndex = getValueFromXmlSubnode<unsigned int>(element, "targetStartIndex", catalogue, true, 0);
336  info.length = getValueFromXmlSubnode<unsigned int>(element, "numberOfElements", catalogue, true, 0);
337  info.nChannels = 0;
338  }
339  else if(type == "redirectedChannel") {
340  info.targetType = LNMBackendRegisterInfo::TargetType::CHANNEL;
341  info.deviceName = getValueFromXmlSubnode<std::string>(element, "targetDevice", catalogue);
342  info.registerName = getValueFromXmlSubnode<std::string>(element, "targetRegister", catalogue);
343  info.channel = getValueFromXmlSubnode<unsigned int>(element, "targetChannel", catalogue);
344  info.firstIndex = getValueFromXmlSubnode<unsigned int>(element, "targetStartIndex", catalogue, true, 0);
345  info.length = getValueFromXmlSubnode<unsigned int>(element, "numberOfElements", catalogue, true, 0);
346  info.nChannels = 1;
347  }
348  else if(type == "redirectedBit") {
349  info.targetType = LNMBackendRegisterInfo::TargetType::BIT;
350  info.deviceName = getValueFromXmlSubnode<std::string>(element, "targetDevice", catalogue);
351  info.registerName = getValueFromXmlSubnode<std::string>(element, "targetRegister", catalogue);
352  info.bit = getValueFromXmlSubnode<unsigned int>(element, "targetBit", catalogue);
353  info.firstIndex = 0;
354  info.length = 0;
355  info.nChannels = 1;
356  }
357  else if(type == "constant") {
358  std::string constantType = getValueFromXmlSubnode<std::string>(element, "type", catalogue);
359  if(constantType == "integer") constantType = "int32";
360  info.targetType = LNMBackendRegisterInfo::TargetType::CONSTANT;
361  info.valueType = DataType(constantType);
362  auto& lnmVariable = _variables[info.name];
363  callForType(info.valueType, [&](auto arg) {
364  boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue =
365  this->getValueVectorFromXmlSubnode<decltype(arg)>(element, "value", catalogue);
366  });
367  lnmVariable.isConstant = true;
368  lnmVariable.valueType = info.valueType;
369  info.firstIndex = 0;
370  info.length = getValueFromXmlSubnode<unsigned int>(element, "numberOfElements", catalogue, true, 1);
371  info.nChannels = 1;
372  info.writeable = false;
373  info.readable = true;
375  }
376  else if(type == "variable") {
377  std::string constantType = getValueFromXmlSubnode<std::string>(element, "type", catalogue);
378  if(constantType == "integer") constantType = "int32";
379  info.targetType = LNMBackendRegisterInfo::TargetType::VARIABLE;
380  info.valueType = DataType(constantType);
381  auto& lnmVariable = _variables[info.name];
382  callForType(info.valueType, [&](auto arg) {
383  boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue =
384  this->getValueVectorFromXmlSubnode<decltype(arg)>(element, "value", catalogue);
385  });
386  lnmVariable.isConstant = false;
387  lnmVariable.valueType = info.valueType;
388  info.firstIndex = 0;
389  info.length = getValueFromXmlSubnode<unsigned int>(element, "numberOfElements", catalogue, true, 1);
390  info.nChannels = 1;
391  info.writeable = true;
392  info.readable = true;
395  }
396  else {
397  parsingError(element, "Wrong logical register type: " + type);
398  }
399 
400  // iterate over children of the register to find plugins
401  for(const auto& child : element->get_children()) {
402  // cast into element, ignore if not an element (e.g. comment)
403  const auto* childElement = dynamic_cast<const xmlpp::Element*>(child);
404  if(!childElement) continue;
405  if(childElement->get_name() != "plugin") continue; // look only for plugins
406 
407  // get name of plugin
408  auto* pluginNameAttr = childElement->get_attribute("name");
409  if(!pluginNameAttr) {
410  parsingError(childElement, "Missing name attribute of 'plugin' tag.");
411  }
412  std::string pluginName = pluginNameAttr->get_value();
413 
414  // collect parameters
415  std::map<std::string, std::string> parameters;
416  for(const auto& paramchild : childElement->get_children()) {
417  // cast into element, ignore if not an element (e.g. comment)
418  const auto* paramElement = dynamic_cast<const xmlpp::Element*>(paramchild);
419  if(!paramElement) continue;
420  if(paramElement->get_name() != "parameter") {
421  parsingError(paramElement, "Unexpected element '" + paramElement->get_name() + "' inside plugin tag.");
422  }
423 
424  // get name of parameter
425  auto* parameterNameAttr = paramElement->get_attribute("name");
426  if(!pluginNameAttr) {
427  parsingError(paramElement, "Missing name attribute of 'parameter' tag.");
428  }
429  std::string parameterName = parameterNameAttr->get_value();
430 
431  // get value of parameter and store in map
432  parameters[parameterName] =
433  getValueFromXmlSubnode<std::string>(childElement, "parameter[@name='" + parameterName + "']", catalogue);
434  }
435 
436  // create instance of plugin and add to the list in the register info
437  info.plugins.push_back(LNMBackend::makePlugin(info, info.plugins.size(), pluginName, parameters));
438  }
439 
440  // add register to catalogue
441  catalogue.addRegister(info);
442  }
443  }
444 
445  /********************************************************************************************************************/
446 
447  void LogicalNameMapParser::parsingError(const xmlpp::Node* node, const std::string& message) {
449  "Error parsing the xlmap file '" + _fileName + "'(" + std::to_string(node->get_line()) + ") : " + message);
450  }
451 
452 } // namespace ChimeraTK
ChimeraTK::LNMBackend::makePlugin
boost::shared_ptr< AccessorPluginBase > makePlugin(LNMBackendRegisterInfo info, size_t pluginIndex, const std::string &name, const std::map< std::string, std::string > &parameters)
Factory function for accessor plugins.
Definition: LNMAccessorPlugin.cc:14
ChimeraTK::LNMBackendRegisterInfo::channel
unsigned int channel
The channel of the target 2D register (if TargetType::CHANNEL)
Definition: LNMBackendRegisterInfo.h:65
ChimeraTK::LNMBackendRegisterInfo::registerName
std::string registerName
The target register name.
Definition: LNMBackendRegisterInfo.h:56
ChimeraTK::LogicalNameMapParser::_parameters
std::map< std::string, std::string > _parameters
actual register info map (register name to target information)
Definition: LogicalNameMapParser.h:65
DeviceBackend.h
ChimeraTK::LogicalNameMapParser::parseElement
void parseElement(const RegisterPath &currentPath, const xmlpp::Element *element, BackendRegisterCatalogue< LNMBackendRegisterInfo > &catalogue)
called inside parseFile() to parse an XML element and its sub-elements recursively
Definition: LogicalNameMapParser.cc:294
ChimeraTK::LogicalNameMapParser::_variables
std::map< std::string, LNMVariable > & _variables
Reference to the variables map inside the LNM backend.
Definition: LogicalNameMapParser.h:68
ChimeraTK::LNMBackendRegisterInfo::plugins
std::vector< boost::shared_ptr< LNMBackend::AccessorPluginBase > > plugins
List of accessor plugins enabled for this register.
Definition: LNMBackendRegisterInfo.h:109
ChimeraTK::LNMBackendRegisterInfo::name
RegisterPath name
Name of the registrer.
Definition: LNMBackendRegisterInfo.h:47
ChimeraTK::LogicalNameMapParser::parsingError
void parsingError(const xmlpp::Node *node, const std::string &message)
throw a parsing error with more information
Definition: LogicalNameMapParser.cc:447
ChimeraTK::LNMBackendRegisterInfo::nChannels
unsigned int nChannels
The number of channels of the logical register.
Definition: LNMBackendRegisterInfo.h:71
ChimeraTK::BackendRegisterCatalogue
Interface for backends to the register catalogue.
Definition: BackendRegisterCatalogue.h:70
ChimeraTK::LNMBackendRegisterInfo::writeable
bool writeable
Flag if the register is writeable.
Definition: LNMBackendRegisterInfo.h:103
ChimeraTK::DataDescriptor
Class describing the actual payload data format of a register in an abstract manner.
Definition: DataDescriptor.h:19
ChimeraTK::LNMBackendRegisterInfo::firstIndex
unsigned int firstIndex
The first index in the range.
Definition: LNMBackendRegisterInfo.h:59
ChimeraTK::LNMBackendRegisterInfo::bit
unsigned int bit
The bit of the target register (if TargetType::BIT)
Definition: LNMBackendRegisterInfo.h:68
ChimeraTK::AccessMode::wait_for_new_data
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
ChimeraTK::LNMBackendRegisterInfo
RegisterInfo structure for the LogicalNameMappingBackend.
Definition: LNMBackendRegisterInfo.h:22
ChimeraTK::LogicalNameMapParser::getValueFromXmlSubnode
ValueType getValueFromXmlSubnode(const xmlpp::Node *node, const std::string &subnodeName, BackendRegisterCatalogue< LNMBackendRegisterInfo > const &catalogue, bool hasDefault=false, ValueType defaultValue=ValueType())
Build a Value object for a given subnode.
ChimeraTK::LNMBackendRegisterInfo::deviceName
std::string deviceName
The target device alias.
Definition: LNMBackendRegisterInfo.h:53
ChimeraTK::BackendRegisterCatalogue::addRegister
void addRegister(const BackendRegisterInfo &registerInfo)
Add register information to the catalogue.
Definition: BackendRegisterCatalogue.h:342
ChimeraTK::LNMBackendRegisterInfo::targetType
TargetType targetType
Type of the target.
Definition: LNMBackendRegisterInfo.h:50
ChimeraTK::DataType
A class to describe which of the supported data types is used.
Definition: SupportedUserTypes.h:599
LogicalNameMapParser.h
ChimeraTK::LogicalNameMapParser::parseFile
BackendRegisterCatalogue< LNMBackendRegisterInfo > parseFile(const std::string &fileName)
parse the given XML file
Definition: LogicalNameMapParser.cc:261
ChimeraTK::LNMBackendRegisterInfo::_dataDescriptor
DataDescriptor _dataDescriptor
Definition: LNMBackendRegisterInfo.h:111
ChimeraTK::LNMBackendRegisterInfo::valueType
DataType valueType
Data type of CONSTANT or VARIABLE type.
Definition: LNMBackendRegisterInfo.h:74
ChimeraTK::callForType
void callForType(const std::type_info &type, LAMBDATYPE lambda)
Helper function for running code which uses some compile-time type that is specified at runtime as a ...
Definition: SupportedUserTypes.h:919
ChimeraTK::RegisterPath
Class to store a register path name.
Definition: RegisterPath.h:16
ChimeraTK::LogicalNameMapParser::getValueVectorFromXmlSubnode
std::vector< ValueType > getValueVectorFromXmlSubnode(const xmlpp::Node *node, const std::string &subnodeName, BackendRegisterCatalogue< LNMBackendRegisterInfo > const &catalogue)
ChimeraTK::LNMBackendRegisterInfo::supportedFlags
AccessModeFlags supportedFlags
Supported AccessMode flags.
Definition: LNMBackendRegisterInfo.h:106
ChimeraTK::BackendRegisterCatalogue::hasRegister
bool hasRegister(const RegisterPath &registerPathName) const override
Check if register with the given path name exists.
Definition: BackendRegisterCatalogue.h:274
ChimeraTK::LNMBackendRegisterInfo::length
unsigned int length
The length of the range (i.e.
Definition: LNMBackendRegisterInfo.h:62
ChimeraTK::BackendRegisterCatalogue::getBackendRegister
virtual BackendRegisterInfo getBackendRegister(const RegisterPath &registerPathName) const
Note: Override this function if backend has "hidden" registers which are not added to the map and hen...
Definition: BackendRegisterCatalogue.h:261
LNMBackendRegisterInfo.h
Exception.h
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::to_string
std::string to_string(Boolean &value)
Definition: SupportedUserTypes.h:59
ChimeraTK::LNMBackendRegisterInfo::readable
bool readable
Flag if the register is readable.
Definition: LNMBackendRegisterInfo.h:99
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51
ChimeraTK::LogicalNameMapParser::_fileName
std::string _fileName
file name of the logical map
Definition: LogicalNameMapParser.h:55