Blue Brain BioExplorer
PropertyMap.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-2024, EPFL/Blue Brain Project
3  *
4  * This file is part of Blue Brain BioExplorer <https://github.com/BlueBrain/BioExplorer>
5  *
6  * This library is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License version 3.0 as published
8  * by the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "PropertyMap.h"
21 
22 #include "Logs.h"
23 #include "utils/StringUtils.h"
24 
25 #include <array>
26 #include <iomanip>
27 
28 #include <boost/make_shared.hpp>
29 #include <boost/program_options.hpp>
31 
32 namespace core
33 {
34 namespace
35 {
36 template <typename TDest, typename TSrc, size_t S>
37 std::array<TDest, S> _convertArray(const std::array<TSrc, S> src)
38 {
39  std::array<TDest, S> dest;
40  for (size_t i = 0; i < S; i++)
41  dest[i] = static_cast<TDest>(src[i]);
42  return dest;
43 }
44 
45 template <typename T, std::size_t N>
46 auto _toStdArray(const std::vector<T>& input)
47 {
48  if (input.size() != N)
49  throw po::error("Not the correct number of args for property");
50  std::array<T, N> array{};
51  std::copy(input.begin(), input.end(), array.begin());
52  return array;
53 }
54 
55 po::options_description _toCommandlineDescription(const PropertyMap& propertyMap)
56 {
57  po::options_description desc(propertyMap.getName());
58  for (const auto& property : propertyMap.getProperties())
59  {
60  po::value_semantic* valueSemantic{nullptr};
61  switch (property->type)
62  {
64  {
65  if (property->enums.empty())
66  valueSemantic = po::value<int32_t>()->default_value(property->get<int32_t>());
67  else
68  valueSemantic = po::value<std::string>()->default_value(property->enums[property->get<int32_t>()]);
69  break;
70  }
72  valueSemantic = po::value<double>()->default_value(property->get<double>());
73  break;
75  valueSemantic = po::value<std::string>()->default_value(property->get<std::string>());
76  break;
78  // default true bools cannot be switched off with bool_switch
79  if (property->get<bool>())
80  valueSemantic = po::value<bool>()->default_value(true);
81  else
82  valueSemantic = po::bool_switch();
83  break;
85  valueSemantic = po::fixed_tokens_value<ints>(2, 2);
86  break;
88  valueSemantic = po::fixed_tokens_value<ints>(3, 3);
89  break;
91  valueSemantic = po::fixed_tokens_value<std::vector<double>>(2, 2);
92  break;
94  valueSemantic = po::fixed_tokens_value<std::vector<double>>(3, 3);
95  break;
97  valueSemantic = po::fixed_tokens_value<std::vector<double>>(4, 4);
98  break;
99  }
100 
101  assert(valueSemantic);
102  const auto dashCaseName = string_utils::camelCaseToSeparated(property->name, '-');
103  desc.add(boost::make_shared<po::option_description>(dashCaseName.c_str(), valueSemantic,
104  property->metaData.description.c_str()));
105  }
106  return desc;
107 }
108 
109 auto _validateEnumValue(const strings& enums, const std::string& value, const std::string& optionName)
110 {
111  auto i = std::find(enums.begin(), enums.end(), value);
112  if (i == enums.end())
113  throw po::error(optionName + " must be one of the following: " + string_utils::join(enums, ", "));
114  return i;
115 }
116 
117 void _commandlineToPropertyMap(const po::variables_map& vm, PropertyMap& propertyMap)
118 {
119  for (const auto& property : propertyMap.getProperties())
120  {
121  const auto dashCaseName = string_utils::camelCaseToSeparated(property->name, '-');
122  if (!vm.count(dashCaseName))
123  continue;
124  switch (property->type)
125  {
126  case Property::Type::Int:
127  if (property->enums.empty())
128  property->set(vm[dashCaseName].as<int32_t>());
129  else
130  {
131  const auto value = vm[dashCaseName].as<std::string>();
132  auto i = _validateEnumValue(property->enums, value, dashCaseName);
133  property->set((int32_t)(i - property->enums.begin()));
134  }
135  break;
137  property->set(vm[dashCaseName].as<double>());
138  break;
140  if (property->enums.empty())
141  property->set(vm[dashCaseName].as<std::string>());
142  else
143  {
144  const auto value = vm[dashCaseName].as<std::string>();
145  auto i = _validateEnumValue(property->enums, value, dashCaseName);
146  property->set(*i);
147  }
148  break;
150  property->set(vm[dashCaseName].as<bool>());
151  break;
153  property->set(_toStdArray<int32_t, 2>(vm[dashCaseName].as<std::vector<int32_t>>()));
154  break;
156  property->set(_toStdArray<double, 2>(vm[dashCaseName].as<std::vector<double>>()));
157  break;
159  property->set(_toStdArray<int32_t, 3>(vm[dashCaseName].as<std::vector<int32_t>>()));
160  break;
162  property->set(_toStdArray<double, 3>(vm[dashCaseName].as<std::vector<double>>()));
163  break;
165  property->set(_toStdArray<double, 4>(vm[dashCaseName].as<std::vector<double>>()));
166  break;
167  }
168  }
169 }
170 } // namespace
171 
172 void Property::_copy(const Property& from)
173 {
174  const auto setValue = [](Property& dest, const Property& src)
175  {
176  switch (dest.type)
177  {
178  case Property::Type::Int:
179  dest.set<int32_t>(static_cast<int32_t>(src.get<double>()));
180  break;
182  dest.set<double>(static_cast<double>(src.get<int32_t>()));
183  break;
185  dest.set<std::array<int32_t, 2>>(_convertArray<int32_t, double, 2>(src.get<std::array<double, 2>>()));
186  break;
188  dest.set<std::array<double, 2>>(_convertArray<double, int32_t, 2>(src.get<std::array<int32_t, 2>>()));
189  break;
191  dest.set<std::array<int32_t, 3>>(_convertArray<int32_t, double, 3>(src.get<std::array<double, 3>>()));
192  break;
194  dest.set<std::array<double, 3>>(_convertArray<double, int32_t, 3>(src.get<std::array<int32_t, 3>>()));
195  break;
196  default:
197  break;
198  };
199  };
200 
201  const auto compatibleTypes = [](Property::Type t0, Property::Type t1)
202  {
203  return (t0 == Property::Type::Int && t1 == Property::Type::Double) ||
204  (t0 == Property::Type::Double && t1 == Property::Type::Int) ||
205  (t0 == Property::Type::Vec2i && t1 == Property::Type::Vec2d) ||
206  (t0 == Property::Type::Vec2d && t1 == Property::Type::Vec2i) ||
207  (t0 == Property::Type::Vec3i && t1 == Property::Type::Vec3d) ||
209  };
210 
211  const auto compatibleEnums = [](const Property& dest, const Property& src)
212  {
213  // If we have a string to int or an int to string we can try to
214  // match the enum value
215  const bool compatible = (dest.type == Property::Type::Int && src.type == Property::Type::String) ||
216  (dest.type == Property::Type::String && src.type == Property::Type::Int);
217  if (dest.enums.empty() || !compatible)
218  return false;
219 
220  // If our source is a string we check if the string exist in destination
221  if (src.type == Property::Type::String)
222  return std::find(dest.enums.begin(), dest.enums.end(), src.get<std::string>()) != dest.enums.end();
223 
224  // We know source is an int so check that the range is inside enum range
225  const int32_t v = src.get<int32_t>();
226  return v >= 0 && v < static_cast<int32_t>(dest.enums.size());
227  };
228 
229  const auto setEnum = [](Property& dest, const Property& src)
230  {
231  if (dest.type == Property::Type::Int)
232  {
233  const auto index =
234  std::find(dest.enums.begin(), dest.enums.end(), src.get<std::string>()) - dest.enums.begin();
235  dest.set<int32_t>(index);
236  }
237  else
238  {
239  const auto idx = src.get<int32_t>();
240  dest.set<std::string>(dest.enums[idx]);
241  }
242  };
243 
244  if (from.type == type)
245  _setData(from._data);
246  else if (compatibleEnums(*this, from))
247  setEnum(*this, from);
248  else if (compatibleTypes(type, from.type))
249  setValue(*this, from);
250  else
251  {
252  throw std::runtime_error("Incompatible types for property '" + name + "'");
253  }
254 }
255 
256 void PropertyMap::merge(const PropertyMap& input)
257 {
258  for (const auto& otherProperty : input.getProperties())
259  {
260  const auto& name = otherProperty->name;
261 
262  if (auto myProperty = find(name))
263  myProperty->_copy(*otherProperty);
264  else
265  setProperty(*otherProperty.get());
266  }
267 }
268 
270 {
271  for (const auto& otherProperty : input.getProperties())
272  {
273  if (auto myProperty = find(otherProperty->name))
274  myProperty->_copy(*otherProperty);
275  }
276 }
277 
278 bool PropertyMap::parse(const int argc, const char** argv)
279 {
280  try
281  {
282  auto desc = _toCommandlineDescription(*this);
283  desc.add_options()("help", "Print this help");
284  const auto parsedOptions = po::command_line_parser(argc, argv).options(desc).run();
285 
286  po::variables_map vm;
287  po::store(parsedOptions, vm);
288  po::notify(vm);
289 
290  if (vm.count("help"))
291  {
292  std::cout << desc << std::endl;
293  return false;
294  }
295 
296  _commandlineToPropertyMap(vm, *this);
297  return true;
298  }
299  catch (const po::error& e)
300  {
301  CORE_ERROR("Failed to parse commandline for " << std::quoted(getName()) << ": " << e.what());
302  return false;
303  }
304 }
305 } // namespace core
void setProperty(const Property &newProperty)
Definition: PropertyMap.h:307
void update(const PropertyMap &input)
const auto & getProperties() const
Definition: PropertyMap.h:373
const auto & getName() const
Definition: PropertyMap.h:293
bool parse(int argc, const char **argv)
void merge(const PropertyMap &input)
auto _toStdArray(const ospcommon::vec_t< T, N > &input)
Definition: OSPRayUtils.cpp:86
std::string join(const std::vector< std::string > &strings, const std::string &joinWith)
Definition: StringUtils.cpp:93
std::string camelCaseToSeparated(const std::string &camelCase, const char separator)
Definition: StringUtils.cpp:57
#define CORE_ERROR(__msg)
Definition: Logs.h:31
std::vector< std::string > strings
Definition: Types.h:44
const Type type
Definition: PropertyMap.h:211
Property(const std::string &name_, const T value, const MetaData &metaData_={})
Definition: PropertyMap.h:113
const std::string name
Definition: PropertyMap.h:209