28 #include <boost/make_shared.hpp>
29 #include <boost/program_options.hpp>
36 template <
typename TDest,
typename TSrc,
size_t S>
37 std::array<TDest, S> _convertArray(
const std::array<TSrc, S> src)
39 std::array<TDest, S> dest;
40 for (
size_t i = 0; i < S; i++)
41 dest[i] =
static_cast<TDest
>(src[i]);
45 template <
typename T, std::
size_t N>
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());
55 po::options_description _toCommandlineDescription(
const PropertyMap& propertyMap)
57 po::options_description desc(propertyMap.getName());
58 for (
const auto& property : propertyMap.getProperties())
60 po::value_semantic* valueSemantic{
nullptr};
61 switch (property->type)
65 if (property->enums.empty())
66 valueSemantic = po::value<int32_t>()->default_value(property->get<int32_t>());
68 valueSemantic = po::value<std::string>()->default_value(property->enums[property->get<int32_t>()]);
72 valueSemantic = po::value<double>()->default_value(property->get<
double>());
75 valueSemantic = po::value<std::string>()->default_value(property->get<std::string>());
79 if (property->get<
bool>())
80 valueSemantic = po::value<bool>()->default_value(
true);
82 valueSemantic = po::bool_switch();
85 valueSemantic = po::fixed_tokens_value<ints>(2, 2);
88 valueSemantic = po::fixed_tokens_value<ints>(3, 3);
91 valueSemantic = po::fixed_tokens_value<std::vector<double>>(2, 2);
94 valueSemantic = po::fixed_tokens_value<std::vector<double>>(3, 3);
97 valueSemantic = po::fixed_tokens_value<std::vector<double>>(4, 4);
101 assert(valueSemantic);
103 desc.add(boost::make_shared<po::option_description>(dashCaseName.c_str(), valueSemantic,
104 property->metaData.description.c_str()));
109 auto _validateEnumValue(
const strings& enums,
const std::string& value,
const std::string& optionName)
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,
", "));
117 void _commandlineToPropertyMap(
const po::variables_map& vm, PropertyMap& propertyMap)
119 for (
const auto& property : propertyMap.getProperties())
122 if (!vm.count(dashCaseName))
124 switch (property->type)
127 if (property->enums.empty())
128 property->set(vm[dashCaseName].as<int32_t>());
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()));
137 property->set(vm[dashCaseName].as<double>());
140 if (property->enums.empty())
141 property->set(vm[dashCaseName].as<std::string>());
144 const auto value = vm[dashCaseName].as<std::string>();
145 auto i = _validateEnumValue(property->enums, value, dashCaseName);
150 property->set(vm[dashCaseName].as<bool>());
153 property->set(_toStdArray<int32_t, 2>(vm[dashCaseName].as<std::vector<int32_t>>()));
156 property->set(_toStdArray<double, 2>(vm[dashCaseName].as<std::vector<double>>()));
159 property->set(_toStdArray<int32_t, 3>(vm[dashCaseName].as<std::vector<int32_t>>()));
162 property->set(_toStdArray<double, 3>(vm[dashCaseName].as<std::vector<double>>()));
165 property->set(_toStdArray<double, 4>(vm[dashCaseName].as<std::vector<double>>()));
172 void Property::_copy(
const Property& from)
179 dest.set<int32_t>(
static_cast<int32_t
>(src.get<
double>()));
182 dest.set<
double>(
static_cast<double>(src.get<int32_t>()));
185 dest.set<std::array<int32_t, 2>>(_convertArray<int32_t, double, 2>(src.get<std::array<double, 2>>()));
188 dest.set<std::array<double, 2>>(_convertArray<double, int32_t, 2>(src.get<std::array<int32_t, 2>>()));
191 dest.set<std::array<int32_t, 3>>(_convertArray<int32_t, double, 3>(src.get<std::array<double, 3>>()));
194 dest.set<std::array<double, 3>>(_convertArray<double, int32_t, 3>(src.get<std::array<int32_t, 3>>()));
217 if (dest.enums.empty() || !compatible)
222 return std::find(dest.enums.begin(), dest.enums.end(), src.get<std::string>()) != dest.enums.end();
225 const int32_t v = src.get<int32_t>();
226 return v >= 0 && v < static_cast<int32_t>(dest.enums.size());
234 std::find(dest.enums.begin(), dest.enums.end(), src.get<std::string>()) - dest.enums.begin();
235 dest.set<int32_t>(index);
239 const auto idx = src.get<int32_t>();
240 dest.set<std::string>(dest.enums[idx]);
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);
252 throw std::runtime_error(
"Incompatible types for property '" +
name +
"'");
260 const auto& name = otherProperty->name;
262 if (
auto myProperty = find(name))
263 myProperty->_copy(*otherProperty);
273 if (
auto myProperty = find(otherProperty->name))
274 myProperty->_copy(*otherProperty);
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();
286 po::variables_map vm;
287 po::store(parsedOptions, vm);
290 if (vm.count(
"help"))
292 std::cout << desc << std::endl;
296 _commandlineToPropertyMap(vm, *
this);
299 catch (
const po::error& e)
301 CORE_ERROR(
"Failed to parse commandline for " << std::quoted(
getName()) <<
": " << e.what());
void setProperty(const Property &newProperty)
void update(const PropertyMap &input)
const auto & getProperties() const
const auto & getName() const
bool parse(int argc, const char **argv)
void merge(const PropertyMap &input)
auto _toStdArray(const ospcommon::vec_t< T, N > &input)
std::string join(const std::vector< std::string > &strings, const std::string &joinWith)
std::string camelCaseToSeparated(const std::string &camelCase, const char separator)
Property(const std::string &name_, const T value, const MetaData &metaData_={})