Chi-Tech
parameter_block.h
Go to the documentation of this file.
1#ifndef CHITECH_PARAMETER_BLOCK_H
2#define CHITECH_PARAMETER_BLOCK_H
3
5
6#include <memory>
7#include <vector>
8#include <string>
9
10namespace chi
11{
12
14{
15 INVALID_VALUE = 0,
16 BOOLEAN = 1,
17 FLOAT = 3,
18 STRING = 4,
19 INTEGER = 5,
20 ARRAY = 98,
21 BLOCK = 99
22};
23
25
26class ParameterBlock;
27
28/**A ParameterBlock is a conceptually simple data structure that supports
29 * a hierarchy of primitive parameters. There really are just 4 member variables
30 * on a ParameterBlock object, they are 1) the type (as an enum), 2) the
31 * name of the block, 3) a pointer to a value (which can only be a primitive
32 * type), and 4) a vector of child parameters.
33 *
34 * If a ParameterBlock has a primitive type, i.e., BOOLEAN, FLOAT, STRING, or
35 * INTEGER, then the value_ptr will contain a pointer to the value of a
36 * primitive type. Otherwise, for types ARRAY and BLOCK, the ParameterBlock
37 * will not have a value_ptr and instead the vector member will contain
38 * sub-parameters.*/
40{
41private:
43 std::string name_;
44 std::unique_ptr<chi_data_types::Varying> value_ptr_ = nullptr;
45 std::vector<ParameterBlock> parameters_;
46 std::string error_origin_scope_ = "Unknown Scope";
47
48public:
49 /**Sets the name of the block.*/
50 void SetBlockName(const std::string& name);
51
52public:
53 // Helpers
54 template <typename T>
55 struct IsBool
56 {
57 static constexpr bool value = std::is_same_v<T, bool>;
58 };
59 template <typename T>
60 struct IsFloat
61 {
62 static constexpr bool value = std::is_floating_point_v<T>;
63 };
64 template <typename T>
65 struct IsString
66 {
67 static constexpr bool value =
68 std::is_same_v<T, std::string> or std::is_same_v<T, const char*>;
69 };
70 template <typename T>
71 struct IsInteger
72 {
73 static constexpr bool value =
74 std::is_integral_v<T> and not std::is_same_v<T, bool>;
75 };
76
77 // Constructors
78 /**Constructs an empty parameter block with the given name and type BLOCK.*/
79 explicit ParameterBlock(const std::string& name = "");
80
81 /**Derived type constructor*/
82 template <typename T>
83 ParameterBlock(const std::string& name, const std::vector<T>& array)
85 {
86 size_t k = 0;
87 for (const T& value : array)
88 AddParameter(std::to_string(k++), value);
89 }
90
91 /**Constructs one of the fundamental types.*/
92 template <typename T>
93 explicit ParameterBlock(const std::string& name, T value) : name_(name)
94 {
95 constexpr bool is_supported = IsBool<T>::value or IsFloat<T>::value or
97
98 static_assert(is_supported, "Value type not supported for parameter block");
99
104
105 value_ptr_ = std::make_unique<chi_data_types::Varying>(value);
106 }
107
108 /**Copy constructor*/
109 ParameterBlock(const ParameterBlock& other);
110
111 /**Copy assignment operator*/
113
114 /**Move constructor*/
115 ParameterBlock(ParameterBlock&& other) noexcept;
116
117 /**Move assignment operator*/
118 ParameterBlock& operator=(ParameterBlock&& other) noexcept;
119
120 // Accessors
121 ParameterBlockType Type() const;
122 /**Returns true if the parameter block comprises a single value of any of
123 * the types BOOLEAN, FLOAT, STRING, INTEGER.*/
124 bool IsScalar() const;
125 /**Returns a string version of the type.*/
126 std::string TypeName() const;
127 std::string Name() const;
128 const chi_data_types::Varying& Value() const;
129 size_t NumParameters() const;
130 /**Returns the sub-parameters of this block.*/
131 const std::vector<ParameterBlock>& Parameters() const;
132 /**Returns whether or not the block has a value. If this block has
133* sub-parameters it should not have a value. This is a good way to
134* check if the block is actually a single value.*/
135 bool HasValue() const;
136
137 // Mutators
138 /**Changes the block type to array, making it accessible via integer
139 * keys.*/
140 void ChangeToArray();
141
142 /**Sets a string to be displayed alongside exceptions that give some
143 * notion of the origin of the error.*/
144 void SetErrorOriginScope(const std::string& scope);
145
146 /**Gets a string that allows error messages to print the scope of an
147 * error.*/
148 std::string GetErrorOriginScope() const { return error_origin_scope_; }
149
150 // Requirements
151 /**Checks that the block is of the given type. If it is not it
152 * will throw an exception `std::logic_error`.*/
153 void RequireBlockTypeIs(ParameterBlockType type) const;
154 void RequireParameterBlockTypeIs(const std::string& param_name,
155 ParameterBlockType type) const
156 {
157 GetParam(param_name).RequireBlockTypeIs(type);
158 }
159 /**Check that the parameter with the given name exists otherwise
160 * throws a `std::logic_error`.*/
161 void RequireParameter(const std::string& param_name) const;
162
163 // utilities
164 /**Adds a parameter to the sub-parameter list.*/
165 void AddParameter(ParameterBlock block);
166 /**Makes a ParameterBlock and adds it to the sub-parameters list.*/
167 template <typename T>
168 void AddParameter(const std::string& name, T value)
169 {
170 AddParameter(ParameterBlock(name, value));
171 }
172
173 /**Sorts the sub-parameter list according to name. This is useful
174 * for regression testing.*/
175 void SortParameters();
176
177 /**Returns true if a parameter with the specified name is in the
178 * list of sub-parameters. Otherwise, false.*/
179 bool Has(const std::string& param_name) const;
180
181 /**Gets a parameter by name.*/
182 ParameterBlock& GetParam(const std::string& param_name);
183 /**Gets a parameter by index.*/
184 ParameterBlock& GetParam(size_t index);
185
186 /**Gets a parameter by name.*/
187 const ParameterBlock& GetParam(const std::string& param_name) const;
188 /**Gets a parameter by index.*/
189 const ParameterBlock& GetParam(size_t index) const;
190
191public:
192 /**Returns the value of the parameter.*/
193 template <typename T>
194 T GetValue() const
195 {
196 if (value_ptr_ == nullptr)
197 throw std::logic_error(error_origin_scope_ +
198 std::string(__PRETTY_FUNCTION__) +
199 ": Value not available for block type " +
201 try
202 {
203 return Value().GetValue<T>();
204 }
205 catch (const std::exception& exc)
206 {
207 throw std::logic_error(error_origin_scope_ + ":" + Name() + " " +
208 exc.what());
209 }
210 }
211
212 /**Fetches the parameter with the given name and returns it value.*/
213 template <typename T>
214 T GetParamValue(const std::string& param_name) const
215 {
216 try
217 {
218 const auto& param = GetParam(param_name);
219 return param.GetValue<T>();
220 }
221 catch (const std::out_of_range& oor)
222 {
223 throw std::out_of_range(
224 error_origin_scope_ + std::string(__PRETTY_FUNCTION__) +
225 ": Parameter \"" + param_name + "\" not present in block");
226 }
227 }
228
229 /**Converts the parameters of an array-type parameter block to a vector of
230 * primitive types and returns it.*/
231 template <typename T>
232 std::vector<T> GetVectorValue() const
233 {
235 throw std::logic_error(error_origin_scope_ +
236 std::string(__PRETTY_FUNCTION__) +
237 ": Invalid type requested for parameter of type " +
239
240 std::vector<T> vec;
241 if (parameters_.empty()) return vec;
242
243 // Check the first sub-param is of the right type
244 const auto& front_param = parameters_.front();
245
246 // Check that all other parameters are of the required type
247 for (const auto& param : parameters_)
248 if (param.Type() != front_param.Type())
249 throw std::logic_error(
250 error_origin_scope_ + " " + std::string(__PRETTY_FUNCTION__) +
251 ": Parameter \"" + name_ +
252 "\", cannot construct vector from block because "
253 "the sub_parameters do not all have the correct type. param->" +
254 ParameterBlockTypeName(param.Type()) + " vs param0->" +
255 ParameterBlockTypeName(front_param.Type()));
256
257 const size_t num_params = parameters_.size();
258 for (size_t k = 0; k < num_params; ++k)
259 {
260 const auto& param = GetParam(k);
261 vec.push_back(param.GetValue<T>());
262 }
263
264 return vec;
265 }
266
267 /**Gets a vector of primitive types from an array-type parameter block
268 * specified as a parameter of the current block.*/
269 template <typename T>
270 std::vector<T> GetParamVectorValue(const std::string& param_name) const
271 {
272 const auto& param = GetParam(param_name);
273 return param.GetVectorValue<T>();
274 }
275
276 // Iterator
277 // clang-format off
279 {
280 public:
282 size_t ref_id;
283
284 iterator(ParameterBlock& in_block, size_t i) :
285 ref_block(in_block), ref_id(i) {}
286
287 iterator operator++() { iterator i = *this; ref_id++; return i; }
288 iterator operator++(int) { ref_id++; return *this; }
289
291 bool operator==(const iterator& rhs) const { return ref_id == rhs.ref_id; }
292 bool operator!=(const iterator& rhs) const { return ref_id != rhs.ref_id; }
293 };
295 {
296 public:
298 size_t ref_id;
299
300 const_iterator(const ParameterBlock& in_block, size_t i) :
301 ref_block(in_block), ref_id(i) {}
302
303 const_iterator operator++(){ const_iterator i = *this; ref_id++; return i; }
304 const_iterator operator++(int) { ref_id++; return *this; }
305
307 bool operator==(const const_iterator& rhs) const
308 { return ref_id == rhs.ref_id; }
309 bool operator!=(const const_iterator& rhs) const
310 { return ref_id != rhs.ref_id; }
311 };
312 // clang-format on
313
314 iterator begin() { return {*this, 0}; }
315 iterator end() { return {*this, parameters_.size()}; }
316
317 const_iterator begin() const { return {*this, 0}; }
318 const_iterator end() const { return {*this, parameters_.size()}; }
319
320 /**Given a reference to a string, recursively travels the parameter
321 * tree and print values into the reference string.*/
322 void RecursiveDumpToString(std::string& outstr,
323 const std::string& offset = "") const;
324
325 void RecursiveDumpToJSON(std::string& outstr) const;
326};
327
328} // namespace chi
329
330#endif // CHITECH_PARAMETER_BLOCK_H
bool operator==(const const_iterator &rhs) const
const ParameterBlock & operator*()
bool operator!=(const const_iterator &rhs) const
const_iterator(const ParameterBlock &in_block, size_t i)
bool operator!=(const iterator &rhs) const
bool operator==(const iterator &rhs) const
iterator(ParameterBlock &in_block, size_t i)
void SetErrorOriginScope(const std::string &scope)
void RequireBlockTypeIs(ParameterBlockType type) const
const chi_data_types::Varying & Value() const
void AddParameter(const std::string &name, T value)
ParameterBlockType type_
void RecursiveDumpToJSON(std::string &outstr) const
bool Has(const std::string &param_name) const
const_iterator end() const
size_t NumParameters() const
void RequireParameterBlockTypeIs(const std::string &param_name, ParameterBlockType type) const
void AddParameter(ParameterBlock block)
std::string error_origin_scope_
std::vector< T > GetVectorValue() const
ParameterBlock(const std::string &name="")
std::string GetErrorOriginScope() const
void RecursiveDumpToString(std::string &outstr, const std::string &offset="") const
void RequireParameter(const std::string &param_name) const
const std::vector< ParameterBlock > & Parameters() const
const_iterator begin() const
ParameterBlock & operator=(const ParameterBlock &other)
std::string TypeName() const
ParameterBlock(const std::string &name, const std::vector< T > &array)
ParameterBlock(const std::string &name, T value)
std::string Name() const
T GetParamValue(const std::string &param_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::vector< T > GetParamVectorValue(const std::string &param_name) const
BoolType< T > GetValue() const
Definition: varying.h:427
std::string ParameterBlockTypeName(ParameterBlockType type)
ParameterBlockType
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value