Chi-Tech
paramater_block.cc
Go to the documentation of this file.
1#include "parameter_block.h"
2
3#include <algorithm>
4#include <sstream>
5
6namespace chi
7{
8// #################################################################
10{
11 switch (type)
12 {
14 return "BOOLEAN";
16 return "FLOAT";
18 return "STRING";
20 return "INTEGER";
22 return "ARRAY";
24 return "BLOCK";
25 default:
26 throw std::logic_error(std::string(__PRETTY_FUNCTION__) +
27 ": No name associated with type");
28 }
29}
30
31// #################################################################
32void ParameterBlock::SetBlockName(const std::string& name) { name_ = name; }
33
34// #################################################################
35ParameterBlock::ParameterBlock(const std::string& name)
36 : type_(ParameterBlockType::BLOCK), name_(name)
37{
38}
39
40// #################################################################
41/**Copy constructor*/
43{
44 type_ = other.type_;
45 name_ = other.name_;
46
47 using namespace chi_data_types;
48 if (other.value_ptr_)
49 value_ptr_ = std::make_unique<Varying>(*other.value_ptr_);
50
53}
54
55// #################################################################
56/**Copy assignment operator*/
58{
59 if (this != &other)
60 {
61 type_ = other.type_;
62 name_ = other.name_;
63
64 using namespace chi_data_types;
65 if (other.value_ptr_)
66 value_ptr_ = std::make_unique<Varying>(*other.value_ptr_);
67
70 }
71
72 return *this;
73}
74
75// #################################################################
76/**Move constructor*/
78{
79 std::swap(type_, other.type_);
80 std::swap(name_, other.name_);
81 std::swap(value_ptr_, other.value_ptr_);
82 std::swap(parameters_, other.parameters_);
83 std::swap(error_origin_scope_, other.error_origin_scope_);
84}
85
86// #################################################################
87/**Move assigment operator.*/
89{
90 if (this != &other)
91 {
92 std::swap(type_, other.type_);
93 std::swap(name_, other.name_);
94 std::swap(value_ptr_, other.value_ptr_);
95 std::swap(parameters_, other.parameters_);
96 std::swap(error_origin_scope_, other.error_origin_scope_);
97 }
98
99 return *this;
100}
101
102// Accessors
104/**Returns true if the parameter block comprises a single value of any of
105 * the types BOOLEAN, FLOAT, STRING, INTEGER.*/
107{
110}
111std::string ParameterBlock::TypeName() const
112{
114}
115std::string ParameterBlock::Name() const { return name_; }
116
117// #################################################################
119{
120 switch (this->Type())
121 {
126 {
127 if (value_ptr_ == nullptr)
128 throw std::runtime_error(
129 error_origin_scope_ + std::string(__PRETTY_FUNCTION__) +
130 ": Uninitialized Varying value for block " + this->Name());
131 return *value_ptr_;
132 }
133 default:
134 throw std::logic_error(
135 error_origin_scope_ + std::string(__PRETTY_FUNCTION__) + ":\"" +
136 this->Name() +
137 "\""
138 " Called for block of type " +
139 ParameterBlockTypeName(this->Type()) + " which has no value.");
140 }
141}
142
143// #################################################################
144/**Returns the number of parameters in a block. This is normally
145 * only useful for the ARRAY type.*/
146size_t ParameterBlock::NumParameters() const { return parameters_.size(); }
147
148// ################################################################
149/**Returns the sub-parameters of this block.*/
150const std::vector<ParameterBlock>& ParameterBlock::Parameters() const
151{
152 return parameters_;
153}
154
155// ################################################################
156/**Returns whether or not the block has a value. If this block has
157 * sub-parameters it should not have a value. This is a good way to
158 * check if the block is actually a single value because some
159 * Parameter blocks can be passed as empty.*/
160bool ParameterBlock::HasValue() const { return value_ptr_ != nullptr; }
161
162// Mutators
163// #################################################################
164/**Changes the block type to array, making it accessible via integer
165 * keys.*/
167{
168 const std::string fname = __PRETTY_FUNCTION__;
169 if (parameters_.empty())
170 {
172 return;
173 }
174
175 const auto& first_param = parameters_.front();
176 for (const auto& param : parameters_)
177 if (param.Type() != first_param.Type())
178 throw std::logic_error(
179 error_origin_scope_ + fname +
180 ": Cannot change ParameterBlock to "
181 "array. It has existing parameters and they are not of the same"
182 "type.");
183
185}
186
187// #################################################################
188// NOLINTBEGIN(misc-no-recursion)
189/**Sets a string to be displayed alongside exceptions that give some
190 * notion of the origin of the error.*/
191void ParameterBlock::SetErrorOriginScope(const std::string& scope)
192{
193 error_origin_scope_ = scope;
194 for (auto& param : parameters_)
195 param.SetErrorOriginScope(scope);
196}
197// NOLINTEND(misc-no-recursion)
198
199// #################################################################
200/**Checks that the block is of the given type. If it is not it
201 * will throw an exception `std::logic_error`.*/
203{
204 if (Type() != type)
205 throw std::logic_error(error_origin_scope_ + ":" + Name() +
206 " Is required to be of type " +
207 ParameterBlockTypeName(type) + " but is " +
209}
210
211/**Check that the parameter with the given name exists otherwise
212 * throws a `std::logic_error`.*/
213void ParameterBlock::RequireParameter(const std::string& param_name) const
214{
215 if (not Has(param_name))
216 throw std::logic_error(error_origin_scope_ + ":" + Name() +
217 " Is required to have parameter " + param_name);
218}
219
220// #################################################################
221/**Adds a parameter to the sub-parameter list.*/
223{
224 for (const auto& param : parameters_)
225 if (param.Name() == block.Name())
226 throw std::invalid_argument(
227 error_origin_scope_ + std::string(__PRETTY_FUNCTION__) +
228 ": Attempting to add duplicate parameter " + param.Name() +
229 " to "
230 "block " +
231 this->Name());
232 parameters_.push_back(std::move(block));
233
235}
236
237// #################################################################
238/**Sorts the sub-parameter list according to name. This is useful
239 * for regression testing.*/
241{
242 struct AlphabeticFunctor
243 {
244 bool operator()(const ParameterBlock& paramA, const ParameterBlock& paramB)
245 {
246 return paramA.Name() < paramB.Name();
247 }
248 };
249
250 struct AlphabeticNumericFunctor
251 {
252 bool operator()(const ParameterBlock& paramA, const ParameterBlock& paramB)
253 {
254 return std::stoi(paramA.Name()) < std::stoi(paramB.Name());
255 }
256 };
257
258 // The different functor here is necessary when the parameters are guaranteed
259 // to have integer names that were converted to strings. It never showed up
260 // because we were not testing with enough values. Essentially "11" < "2" in
261 // the realm of strings but not in integer world.
262 if (this->Type() != ParameterBlockType::ARRAY)
263 std::sort(parameters_.begin(), parameters_.end(), AlphabeticFunctor());
264 else
265 std::sort(
266 parameters_.begin(), parameters_.end(), AlphabeticNumericFunctor());
267}
268
269// #################################################################
270/**Returns true if a parameter with the specified name is in the
271 * list of sub-parameters. Otherwise, false.*/
272bool ParameterBlock::Has(const std::string& param_name) const
273{
274 return std::any_of(parameters_.begin(),
275 parameters_.end(),
276 [&param_name](const ParameterBlock& param)
277 { return param.name_ == param_name; });
278}
279
280// #################################################################
281/**Gets a parameter by name.*/
282ParameterBlock& ParameterBlock::GetParam(const std::string& param_name)
283{
284 for (auto& param : parameters_)
285 if (param.Name() == param_name) return param;
286
287 throw std::out_of_range(error_origin_scope_ + ":" +
288 std::string(__PRETTY_FUNCTION__) + ": Parameter \"" +
289 param_name + "\" not present in block");
290}
291
292// #################################################################
293/**Gets a parameter by index.*/
295{
296 try
297 {
298 return parameters_.at(index);
299 }
300 catch (const std::out_of_range& oor)
301 {
302 throw std::out_of_range(error_origin_scope_ +
303 std::string(__PRETTY_FUNCTION__) +
304 ": Parameter with index " + std::to_string(index) +
305 " not present in block");
306 }
307}
308
309// #################################################################
310/**Gets a parameter by name.*/
311const ParameterBlock&
312ParameterBlock::GetParam(const std::string& param_name) const
313{
314 for (const auto& param : parameters_)
315 if (param.Name() == param_name) return param;
316
317 throw std::out_of_range(error_origin_scope_ +
318 std::string(__PRETTY_FUNCTION__) + ": Parameter \"" +
319 param_name + "\" not present in block");
320}
321
322// #################################################################
323/**Gets a parameter by index.*/
324const ParameterBlock& ParameterBlock::GetParam(size_t index) const
325{
326 try
327 {
328 return parameters_.at(index);
329 }
330 catch (const std::out_of_range& oor)
331 {
332 throw std::out_of_range(error_origin_scope_ +
333 std::string(__PRETTY_FUNCTION__) +
334 ": Parameter with index " + std::to_string(index) +
335 " not present in block");
336 }
337}
338
339// #################################################################
340// NOLINTBEGIN(misc-no-recursion)
341/**Print the block tree structure into a designated string.*/
343 const std::string& offset) const
344{
345 outstr += offset + this->Name() + " = \n";
346 outstr += offset + "{\n";
347
348 if (HasValue()) outstr += value_ptr_->PrintStr();
349
350 for (const auto& param : parameters_)
351 {
352
353 switch (param.Type())
354 {
356 {
357 outstr += offset + " " + param.Name() + " = ";
358 const bool value = param.Value().BoolValue();
359 outstr += std::string(value ? "true" : "false") + ",\n";
360 break;
361 }
363 {
364 outstr += offset + " " + param.Name() + " = ";
365 const double value = param.Value().FloatValue();
366 outstr += std::to_string(value) + ",\n";
367 break;
368 }
370 {
371 outstr += offset + " " + param.Name() + " = ";
372 const auto& value = param.Value().StringValue();
373 outstr += "\"" + value + "\",\n";
374 break;
375 }
377 {
378 outstr += offset + " " + param.Name() + " = ";
379 const int64_t value = param.Value().IntegerValue();
380 outstr += std::to_string(value) + ",\n";
381 break;
382 }
385 {
386 param.RecursiveDumpToString(outstr, offset + " ");
387 break;
388 }
389 default:
390 break;
391 }
392 } // for parameter
393
394 outstr += offset + "}\n";
395}
396// NOLINTEND(misc-no-recursion)
397
398// #################################################################
399// NOLINTBEGIN(misc-no-recursion)
400/**Print the block tree structure into a designated string.*/
401void ParameterBlock::RecursiveDumpToJSON(std::string& outstr) const
402{
403 if (HasValue())
404 {
405 outstr += value_ptr_->PrintStr(/*with_type=*/false);
406 return;
407 }
408
409 outstr += (this->Type() == ParameterBlockType::ARRAY ? "[" : "{");
410 for (const auto& param : parameters_)
411 {
412
413 switch (param.Type())
414 {
416 {
417 outstr += "\"" + param.Name() + "\" = ";
418 const bool value = param.Value().BoolValue();
419 outstr += std::string(value ? "true" : "false") + ",\n";
420 break;
421 }
423 {
424 outstr += "\"" + param.Name() + "\" = ";
425 const double value = param.Value().FloatValue();
426 outstr += std::to_string(value) + ",\n";
427 break;
428 }
430 {
431 outstr += "\"" + param.Name() + "\" = ";
432 const auto& value = param.Value().StringValue();
433 outstr += "\"" + value + "\",\n";
434 break;
435 }
437 {
438 outstr += "\"" + param.Name() + "\" = ";
439 const int64_t value = param.Value().IntegerValue();
440 outstr += std::to_string(value) + ",\n";
441 break;
442 }
445 {
446 param.RecursiveDumpToJSON(outstr);
447 break;
448 }
449 default:
450 break;
451 }
452 } // for parameter
453 outstr += (this->Type() == ParameterBlockType::ARRAY ? "]" : "}");
454}
455// NOLINTEND(misc-no-recursion)
456
457} // namespace chi
void SetErrorOriginScope(const std::string &scope)
void RequireBlockTypeIs(ParameterBlockType type) const
const chi_data_types::Varying & Value() const
ParameterBlockType type_
void RecursiveDumpToJSON(std::string &outstr) const
bool Has(const std::string &param_name) const
size_t NumParameters() const
void AddParameter(ParameterBlock block)
std::string error_origin_scope_
ParameterBlock(const std::string &name="")
void RecursiveDumpToString(std::string &outstr, const std::string &offset="") const
void RequireParameter(const std::string &param_name) const
const std::vector< ParameterBlock > & Parameters() const
ParameterBlock & operator=(const ParameterBlock &other)
std::string TypeName() const
std::string Name() const
std::unique_ptr< chi_data_types::Varying > value_ptr_
void SetBlockName(const std::string &name)
std::vector< ParameterBlock > parameters_
ParameterBlockType Type() const
ParameterBlock & GetParam(const std::string &param_name)
std::string ParameterBlockTypeName(ParameterBlockType type)
ParameterBlockType