1 #ifndef slic3r_Config_hpp_
2 #define slic3r_Config_hpp_
3
4 #include <assert.h>
5 #include <map>
6 #include <climits>
7 #include <cstdio>
8 #include <cstdlib>
9 #include <functional>
10 #include <iostream>
11 #include <stdexcept>
12 #include <string>
13 #include <vector>
14 #include "libslic3r.h"
15 #include "clonable_ptr.hpp"
16 #include "Exception.hpp"
17 #include "Point.hpp"
18
19 #include <boost/algorithm/string/predicate.hpp>
20 #include <boost/algorithm/string/trim.hpp>
21 #include <boost/format/format_fwd.hpp>
22 #include <boost/property_tree/ptree_fwd.hpp>
23
24 #include <cereal/access.hpp>
25 #include <cereal/types/base_class.hpp>
26
27 namespace Slic3r {
28
29 // Name of the configuration option.
30 typedef std::string t_config_option_key;
31 typedef std::vector<std::string> t_config_option_keys;
32
33 extern std::string escape_string_cstyle(const std::string &str);
34 extern std::string escape_strings_cstyle(const std::vector<std::string> &strs);
35 extern bool unescape_string_cstyle(const std::string &str, std::string &out);
36 extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
37
38 extern std::string escape_ampersand(const std::string& str);
39
40 namespace ConfigHelpers {
looks_like_enum_value(std::string value)41 inline bool looks_like_enum_value(std::string value)
42 {
43 boost::trim(value);
44 if (value.empty() || value.size() > 64 || ! isalpha(value.front()))
45 return false;
46 for (const char c : value)
47 if (! (isalnum(c) || c == '_' || c == '-'))
48 return false;
49 return true;
50 }
51
enum_looks_like_true_value(std::string value)52 inline bool enum_looks_like_true_value(std::string value) {
53 boost::trim(value);
54 return boost::iequals(value, "enabled") || boost::iequals(value, "on");
55 }
56
57 enum class DeserializationSubstitution {
58 Disabled,
59 DefaultsToFalse,
60 DefaultsToTrue
61 };
62
63 enum class DeserializationResult {
64 Loaded,
65 Substituted,
66 Failed,
67 };
68 };
69
70 // Base for all exceptions thrown by the configuration layer.
71 class ConfigurationError : public Slic3r::RuntimeError {
72 public:
73 using RuntimeError::RuntimeError;
74 };
75
76 // Specialization of std::exception to indicate that an unknown config option has been encountered.
77 class UnknownOptionException : public ConfigurationError {
78 public:
UnknownOptionException()79 UnknownOptionException() :
80 ConfigurationError("Unknown option exception") {}
UnknownOptionException(const std::string & opt_key)81 UnknownOptionException(const std::string &opt_key) :
82 ConfigurationError(std::string("Unknown option exception: ") + opt_key) {}
83 };
84
85 // Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
86 class NoDefinitionException : public ConfigurationError
87 {
88 public:
NoDefinitionException()89 NoDefinitionException() :
90 ConfigurationError("No definition exception") {}
NoDefinitionException(const std::string & opt_key)91 NoDefinitionException(const std::string &opt_key) :
92 ConfigurationError(std::string("No definition exception: ") + opt_key) {}
93 };
94
95 // Indicate that an unsupported accessor was called on a config option.
96 class BadOptionTypeException : public ConfigurationError
97 {
98 public:
BadOptionTypeException()99 BadOptionTypeException() : ConfigurationError("Bad option type exception") {}
BadOptionTypeException(const std::string & message)100 BadOptionTypeException(const std::string &message) : ConfigurationError(message) {}
BadOptionTypeException(const char * message)101 BadOptionTypeException(const char* message) : ConfigurationError(message) {}
102 };
103
104 // Indicate that an option has been deserialized from an invalid value.
105 class BadOptionValueException : public ConfigurationError
106 {
107 public:
BadOptionValueException()108 BadOptionValueException() : ConfigurationError("Bad option value exception") {}
BadOptionValueException(const std::string & message)109 BadOptionValueException(const std::string &message) : ConfigurationError(message) {}
BadOptionValueException(const char * message)110 BadOptionValueException(const char* message) : ConfigurationError(message) {}
111 };
112
113 // Type of a configuration value.
114 enum ConfigOptionType {
115 coVectorType = 0x4000,
116 coNone = 0,
117 // single float
118 coFloat = 1,
119 // vector of floats
120 coFloats = coFloat + coVectorType,
121 // single int
122 coInt = 2,
123 // vector of ints
124 coInts = coInt + coVectorType,
125 // single string
126 coString = 3,
127 // vector of strings
128 coStrings = coString + coVectorType,
129 // percent value. Currently only used for infill.
130 coPercent = 4,
131 // percents value. Currently used for retract before wipe only.
132 coPercents = coPercent + coVectorType,
133 // a fraction or an absolute value
134 coFloatOrPercent = 5,
135 // vector of the above
136 coFloatsOrPercents = coFloatOrPercent + coVectorType,
137 // single 2d point (Point2f). Currently not used.
138 coPoint = 6,
139 // vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
140 coPoints = coPoint + coVectorType,
141 coPoint3 = 7,
142 // coPoint3s = coPoint3 + coVectorType,
143 // single boolean value
144 coBool = 8,
145 // vector of boolean values
146 coBools = coBool + coVectorType,
147 // a generic enum
148 coEnum = 9,
149 };
150
151 enum ConfigOptionMode {
152 comSimple = 0,
153 comAdvanced,
154 comExpert
155 };
156
157 enum PrinterTechnology : unsigned char
158 {
159 // Fused Filament Fabrication
160 ptFFF,
161 // Stereolitography
162 ptSLA,
163 // Unknown, useful for command line processing
164 ptUnknown,
165 // Any technology, useful for parameters compatible with both ptFFF and ptSLA
166 ptAny
167 };
168
169 enum ForwardCompatibilitySubstitutionRule
170 {
171 // Disable susbtitution, throw exception if an option value is not recognized.
172 Disable,
173 // Enable substitution of an unknown option value with default. Log the substitution.
174 Enable,
175 // Enable substitution of an unknown option value with default. Don't log the substitution.
176 EnableSilent,
177 // Enable substitution of an unknown option value with default. Log substitutions in user profiles, don't log substitutions in system profiles.
178 EnableSystemSilent,
179 // Enable silent substitution of an unknown option value with default when loading user profiles. Throw on an unknown option value in a system profile.
180 EnableSilentDisableSystem,
181 };
182
183 class ConfigOption;
184 class ConfigOptionDef;
185 // For forward definition of ConfigOption in ConfigOptionUniquePtr, we have to define a custom deleter.
186 struct ConfigOptionDeleter { void operator()(ConfigOption* p); };
187 using ConfigOptionUniquePtr = std::unique_ptr<ConfigOption, ConfigOptionDeleter>;
188
189 // When parsing a configuration value, if the old_value is not understood by this PrusaSlicer version,
190 // it is being substituted with some default value that this PrusaSlicer could work with.
191 // This structure serves to inform the user about the substitutions having been done during file import.
192 struct ConfigSubstitution {
193 const ConfigOptionDef *opt_def { nullptr };
194 std::string old_value;
195 ConfigOptionUniquePtr new_value;
196 };
197
198 using ConfigSubstitutions = std::vector<ConfigSubstitution>;
199
200 // Filled in by ConfigBase::set_deserialize_raw(), which based on "rule" either bails out
201 // or performs substitutions when encountering an unknown configuration value.
202 struct ConfigSubstitutionContext
203 {
ConfigSubstitutionContextSlic3r::ConfigSubstitutionContext204 ConfigSubstitutionContext(ForwardCompatibilitySubstitutionRule rl) : rule(rl) {}
emptySlic3r::ConfigSubstitutionContext205 bool empty() const throw() { return substitutions.empty(); }
206
207 ForwardCompatibilitySubstitutionRule rule;
208 ConfigSubstitutions substitutions;
209 };
210
211 // A generic value of a configuration option.
212 class ConfigOption {
213 public:
~ConfigOption()214 virtual ~ConfigOption() {}
215
216 virtual ConfigOptionType type() const = 0;
217 virtual std::string serialize() const = 0;
218 virtual bool deserialize(const std::string &str, bool append = false) = 0;
219 virtual ConfigOption* clone() const = 0;
220 // Set a value from a ConfigOption. The two options should be compatible.
221 virtual void set(const ConfigOption *option) = 0;
getInt() const222 virtual int getInt() const { throw BadOptionTypeException("Calling ConfigOption::getInt on a non-int ConfigOption"); }
getFloat() const223 virtual double getFloat() const { throw BadOptionTypeException("Calling ConfigOption::getFloat on a non-float ConfigOption"); }
getBool() const224 virtual bool getBool() const { throw BadOptionTypeException("Calling ConfigOption::getBool on a non-boolean ConfigOption"); }
setInt(int)225 virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); }
226 virtual bool operator==(const ConfigOption &rhs) const = 0;
operator !=(const ConfigOption & rhs) const227 bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); }
is_scalar() const228 bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; }
is_vector() const229 bool is_vector() const { return ! this->is_scalar(); }
230 // If this option is nullable, then it may have its value or values set to nil.
nullable() const231 virtual bool nullable() const { return false; }
232 // A scalar is nil, or all values of a vector are nil.
is_nil() const233 virtual bool is_nil() const { return false; }
234 // Is this option overridden by another option?
235 // An option overrides another option if it is not nil and not equal.
overriden_by(const ConfigOption * rhs) const236 virtual bool overriden_by(const ConfigOption *rhs) const {
237 assert(! this->nullable() && ! rhs->nullable());
238 return *this != *rhs;
239 }
240 // Apply an override option, possibly a nullable one.
apply_override(const ConfigOption * rhs)241 virtual bool apply_override(const ConfigOption *rhs) {
242 if (*this == *rhs)
243 return false;
244 *this = *rhs;
245 return true;
246 }
247 };
248
249 typedef ConfigOption* ConfigOptionPtr;
250 typedef const ConfigOption* ConfigOptionConstPtr;
251
252 // Value of a single valued option (bool, int, float, string, point, enum)
253 template <class T>
254 class ConfigOptionSingle : public ConfigOption {
255 public:
256 T value;
ConfigOptionSingle(T value)257 explicit ConfigOptionSingle(T value) : value(value) {}
operator T() const258 operator T() const { return this->value; }
259
set(const ConfigOption * rhs)260 void set(const ConfigOption *rhs) override
261 {
262 if (rhs->type() != this->type())
263 throw ConfigurationError("ConfigOptionSingle: Assigning an incompatible type");
264 assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs));
265 this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
266 }
267
operator ==(const ConfigOption & rhs) const268 bool operator==(const ConfigOption &rhs) const override
269 {
270 if (rhs.type() != this->type())
271 throw ConfigurationError("ConfigOptionSingle: Comparing incompatible types");
272 assert(dynamic_cast<const ConfigOptionSingle<T>*>(&rhs));
273 return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
274 }
275
operator ==(const T & rhs) const276 bool operator==(const T &rhs) const { return this->value == rhs; }
operator !=(const T & rhs) const277 bool operator!=(const T &rhs) const { return this->value != rhs; }
278
279 private:
280 friend class cereal::access;
serialize(Archive & ar)281 template<class Archive> void serialize(Archive & ar) { ar(this->value); }
282 };
283
284 // Value of a vector valued option (bools, ints, floats, strings, points)
285 class ConfigOptionVectorBase : public ConfigOption {
286 public:
287 // Currently used only to initialize the PlaceholderParser.
288 virtual std::vector<std::string> vserialize() const = 0;
289 // Set from a vector of ConfigOptions.
290 // If the rhs ConfigOption is scalar, then its value is used,
291 // otherwise for each of rhs, the first value of a vector is used.
292 // This function is useful to collect values for multiple extrder / filament settings.
293 virtual void set(const std::vector<const ConfigOption*> &rhs) = 0;
294 // Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
295 // This function is useful to split values from multiple extrder / filament settings into separate configurations.
296 virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0;
297 // Resize the vector of values, copy the newly added values from opt_default if provided.
298 virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
299 // Clear the values vector.
300 virtual void clear() = 0;
301
302 // Get size of this vector.
303 virtual size_t size() const = 0;
304 // Is this vector empty?
305 virtual bool empty() const = 0;
306 // Is the value nil? That should only be possible if this->nullable().
307 virtual bool is_nil(size_t idx) const = 0;
308
309 // We just overloaded and hid two base class virtual methods.
310 // Let's show it was intentional (warnings).
311 using ConfigOption::set;
312 using ConfigOption::is_nil;
313
314
315 protected:
316 // Used to verify type compatibility when assigning to / from a scalar ConfigOption.
scalar_type() const317 ConfigOptionType scalar_type() const { return static_cast<ConfigOptionType>(this->type() - coVectorType); }
318 };
319
320 // Value of a vector valued option (bools, ints, floats, strings, points), template
321 template <class T>
322 class ConfigOptionVector : public ConfigOptionVectorBase
323 {
324 public:
ConfigOptionVector()325 ConfigOptionVector() {}
ConfigOptionVector(size_t n,const T & value)326 explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {}
ConfigOptionVector(std::initializer_list<T> il)327 explicit ConfigOptionVector(std::initializer_list<T> il) : values(std::move(il)) {}
ConfigOptionVector(const std::vector<T> & values)328 explicit ConfigOptionVector(const std::vector<T> &values) : values(values) {}
ConfigOptionVector(std::vector<T> && values)329 explicit ConfigOptionVector(std::vector<T> &&values) : values(std::move(values)) {}
330 std::vector<T> values;
331
set(const ConfigOption * rhs)332 void set(const ConfigOption *rhs) override
333 {
334 if (rhs->type() != this->type())
335 throw ConfigurationError("ConfigOptionVector: Assigning an incompatible type");
336 assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs));
337 this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
338 }
339
340 // Set from a vector of ConfigOptions.
341 // If the rhs ConfigOption is scalar, then its value is used,
342 // otherwise for each of rhs, the first value of a vector is used.
343 // This function is useful to collect values for multiple extrder / filament settings.
set(const std::vector<const ConfigOption * > & rhs)344 void set(const std::vector<const ConfigOption*> &rhs) override
345 {
346 this->values.clear();
347 this->values.reserve(rhs.size());
348 for (const ConfigOption *opt : rhs) {
349 if (opt->type() == this->type()) {
350 auto other = static_cast<const ConfigOptionVector<T>*>(opt);
351 if (other->values.empty())
352 throw ConfigurationError("ConfigOptionVector::set(): Assigning from an empty vector");
353 this->values.emplace_back(other->values.front());
354 } else if (opt->type() == this->scalar_type())
355 this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value);
356 else
357 throw ConfigurationError("ConfigOptionVector::set():: Assigning an incompatible type");
358 }
359 }
360
361 // Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
362 // This function is useful to split values from multiple extrder / filament settings into separate configurations.
set_at(const ConfigOption * rhs,size_t i,size_t j)363 void set_at(const ConfigOption *rhs, size_t i, size_t j) override
364 {
365 // It is expected that the vector value has at least one value, which is the default, if not overwritten.
366 assert(! this->values.empty());
367 if (this->values.size() <= i) {
368 // Resize this vector, fill in the new vector fields with the copy of the first field.
369 T v = this->values.front();
370 this->values.resize(i + 1, v);
371 }
372 if (rhs->type() == this->type()) {
373 // Assign the first value of the rhs vector.
374 auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
375 if (other->values.empty())
376 throw ConfigurationError("ConfigOptionVector::set_at(): Assigning from an empty vector");
377 this->values[i] = other->get_at(j);
378 } else if (rhs->type() == this->scalar_type())
379 this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
380 else
381 throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type");
382 }
383
get_at(size_t i) const384 const T& get_at(size_t i) const
385 {
386 assert(! this->values.empty());
387 return (i < this->values.size()) ? this->values[i] : this->values.front();
388 }
389
get_at(size_t i)390 T& get_at(size_t i) { return const_cast<T&>(std::as_const(*this).get_at(i)); }
391
392 // Resize this vector by duplicating the /*last*/first value.
393 // If the current vector is empty, the default value is used instead.
resize(size_t n,const ConfigOption * opt_default=nullptr)394 void resize(size_t n, const ConfigOption *opt_default = nullptr) override
395 {
396 assert(opt_default == nullptr || opt_default->is_vector());
397 // assert(opt_default == nullptr || dynamic_cast<ConfigOptionVector<T>>(opt_default));
398 assert(! this->values.empty() || opt_default != nullptr);
399 if (n == 0)
400 this->values.clear();
401 else if (n < this->values.size())
402 this->values.erase(this->values.begin() + n, this->values.end());
403 else if (n > this->values.size()) {
404 if (this->values.empty()) {
405 if (opt_default == nullptr)
406 throw ConfigurationError("ConfigOptionVector::resize(): No default value provided.");
407 if (opt_default->type() != this->type())
408 throw ConfigurationError("ConfigOptionVector::resize(): Extending with an incompatible type.");
409 this->values.resize(n, static_cast<const ConfigOptionVector<T>*>(opt_default)->values.front());
410 } else {
411 // Resize by duplicating the last value.
412 this->values.resize(n, this->values./*back*/front());
413 }
414 }
415 }
416
417 // Clear the values vector.
clear()418 void clear() override { this->values.clear(); }
size() const419 size_t size() const override { return this->values.size(); }
empty() const420 bool empty() const override { return this->values.empty(); }
421
operator ==(const ConfigOption & rhs) const422 bool operator==(const ConfigOption &rhs) const override
423 {
424 if (rhs.type() != this->type())
425 throw ConfigurationError("ConfigOptionVector: Comparing incompatible types");
426 assert(dynamic_cast<const ConfigOptionVector<T>*>(&rhs));
427 return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
428 }
429
operator ==(const std::vector<T> & rhs) const430 bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; }
operator !=(const std::vector<T> & rhs) const431 bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; }
432
433 // Is this option overridden by another option?
434 // An option overrides another option if it is not nil and not equal.
overriden_by(const ConfigOption * rhs) const435 bool overriden_by(const ConfigOption *rhs) const override {
436 if (this->nullable())
437 throw ConfigurationError("Cannot override a nullable ConfigOption.");
438 if (rhs->type() != this->type())
439 throw ConfigurationError("ConfigOptionVector.overriden_by() applied to different types.");
440 auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
441 if (! rhs->nullable())
442 // Overridding a non-nullable object with another non-nullable object.
443 return this->values != rhs_vec->values;
444 size_t i = 0;
445 size_t cnt = std::min(this->size(), rhs_vec->size());
446 for (; i < cnt; ++ i)
447 if (! rhs_vec->is_nil(i) && this->values[i] != rhs_vec->values[i])
448 return true;
449 for (; i < rhs_vec->size(); ++ i)
450 if (! rhs_vec->is_nil(i))
451 return true;
452 return false;
453 }
454 // Apply an override option, possibly a nullable one.
apply_override(const ConfigOption * rhs)455 bool apply_override(const ConfigOption *rhs) override {
456 if (this->nullable())
457 throw ConfigurationError("Cannot override a nullable ConfigOption.");
458 if (rhs->type() != this->type())
459 throw ConfigurationError("ConfigOptionVector.apply_override() applied to different types.");
460 auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
461 if (! rhs->nullable()) {
462 // Overridding a non-nullable object with another non-nullable object.
463 if (this->values != rhs_vec->values) {
464 this->values = rhs_vec->values;
465 return true;
466 }
467 return false;
468 }
469 size_t i = 0;
470 size_t cnt = std::min(this->size(), rhs_vec->size());
471 bool modified = false;
472 for (; i < cnt; ++ i)
473 if (! rhs_vec->is_nil(i) && this->values[i] != rhs_vec->values[i]) {
474 this->values[i] = rhs_vec->values[i];
475 modified = true;
476 }
477 for (; i < rhs_vec->size(); ++ i)
478 if (! rhs_vec->is_nil(i)) {
479 if (this->values.empty())
480 this->values.resize(i + 1);
481 else
482 this->values.resize(i + 1, this->values.front());
483 this->values[i] = rhs_vec->values[i];
484 modified = true;
485 }
486 return modified;
487 }
488
489 private:
490 friend class cereal::access;
serialize(Archive & ar)491 template<class Archive> void serialize(Archive & ar) { ar(this->values); }
492 };
493
494 class ConfigOptionFloat : public ConfigOptionSingle<double>
495 {
496 public:
ConfigOptionFloat()497 ConfigOptionFloat() : ConfigOptionSingle<double>(0) {}
ConfigOptionFloat(double _value)498 explicit ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {}
499
static_type()500 static ConfigOptionType static_type() { return coFloat; }
type() const501 ConfigOptionType type() const override { return static_type(); }
getFloat() const502 double getFloat() const override { return this->value; }
clone() const503 ConfigOption* clone() const override { return new ConfigOptionFloat(*this); }
operator ==(const ConfigOptionFloat & rhs) const504 bool operator==(const ConfigOptionFloat &rhs) const { return this->value == rhs.value; }
505
serialize() const506 std::string serialize() const override
507 {
508 std::ostringstream ss;
509 ss << this->value;
510 return ss.str();
511 }
512
deserialize(const std::string & str,bool append=false)513 bool deserialize(const std::string &str, bool append = false) override
514 {
515 UNUSED(append);
516 std::istringstream iss(str);
517 iss >> this->value;
518 return !iss.fail();
519 }
520
operator =(const ConfigOption * opt)521 ConfigOptionFloat& operator=(const ConfigOption *opt)
522 {
523 this->set(opt);
524 return *this;
525 }
526
527 private:
528 friend class cereal::access;
serialize(Archive & ar)529 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<double>>(this)); }
530 };
531
532 template<bool NULLABLE>
533 class ConfigOptionFloatsTempl : public ConfigOptionVector<double>
534 {
535 public:
ConfigOptionFloatsTempl()536 ConfigOptionFloatsTempl() : ConfigOptionVector<double>() {}
ConfigOptionFloatsTempl(size_t n,double value)537 explicit ConfigOptionFloatsTempl(size_t n, double value) : ConfigOptionVector<double>(n, value) {}
ConfigOptionFloatsTempl(std::initializer_list<double> il)538 explicit ConfigOptionFloatsTempl(std::initializer_list<double> il) : ConfigOptionVector<double>(std::move(il)) {}
ConfigOptionFloatsTempl(const std::vector<double> & vec)539 explicit ConfigOptionFloatsTempl(const std::vector<double> &vec) : ConfigOptionVector<double>(vec) {}
ConfigOptionFloatsTempl(std::vector<double> && vec)540 explicit ConfigOptionFloatsTempl(std::vector<double> &&vec) : ConfigOptionVector<double>(std::move(vec)) {}
541
static_type()542 static ConfigOptionType static_type() { return coFloats; }
type() const543 ConfigOptionType type() const override { return static_type(); }
clone() const544 ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); }
operator ==(const ConfigOptionFloatsTempl & rhs) const545 bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
operator ==(const ConfigOption & rhs) const546 bool operator==(const ConfigOption &rhs) const override {
547 if (rhs.type() != this->type())
548 throw ConfigurationError("ConfigOptionFloatsTempl: Comparing incompatible types");
549 assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs));
550 return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values);
551 }
552 // Could a special "nil" value be stored inside the vector, indicating undefined value?
nullable() const553 bool nullable() const override { return NULLABLE; }
554 // Special "nil" value to be stored into the vector if this->supports_nil().
nil_value()555 static double nil_value() { return std::numeric_limits<double>::quiet_NaN(); }
556 // A scalar is nil, or all values of a vector are nil.
is_nil() const557 bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v)) return false; return true; }
is_nil(size_t idx) const558 bool is_nil(size_t idx) const override { return std::isnan(this->values[idx]); }
559
serialize() const560 std::string serialize() const override
561 {
562 std::ostringstream ss;
563 for (const double &v : this->values) {
564 if (&v != &this->values.front())
565 ss << ",";
566 serialize_single_value(ss, v);
567 }
568 return ss.str();
569 }
570
vserialize() const571 std::vector<std::string> vserialize() const override
572 {
573 std::vector<std::string> vv;
574 vv.reserve(this->values.size());
575 for (const double v : this->values) {
576 std::ostringstream ss;
577 serialize_single_value(ss, v);
578 vv.push_back(ss.str());
579 }
580 return vv;
581 }
582
deserialize(const std::string & str,bool append=false)583 bool deserialize(const std::string &str, bool append = false) override
584 {
585 if (! append)
586 this->values.clear();
587 std::istringstream is(str);
588 std::string item_str;
589 while (std::getline(is, item_str, ',')) {
590 boost::trim(item_str);
591 if (item_str == "nil") {
592 if (NULLABLE)
593 this->values.push_back(nil_value());
594 else
595 throw ConfigurationError("Deserializing nil into a non-nullable object");
596 } else {
597 std::istringstream iss(item_str);
598 double value;
599 iss >> value;
600 this->values.push_back(value);
601 }
602 }
603 return true;
604 }
605
operator =(const ConfigOption * opt)606 ConfigOptionFloatsTempl& operator=(const ConfigOption *opt)
607 {
608 this->set(opt);
609 return *this;
610 }
611
612 protected:
serialize_single_value(std::ostringstream & ss,const double v) const613 void serialize_single_value(std::ostringstream &ss, const double v) const {
614 if (std::isfinite(v))
615 ss << v;
616 else if (std::isnan(v)) {
617 if (NULLABLE)
618 ss << "nil";
619 else
620 throw ConfigurationError("Serializing NaN");
621 } else
622 throw ConfigurationError("Serializing invalid number");
623 }
vectors_equal(const std::vector<double> & v1,const std::vector<double> & v2)624 static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
625 if (NULLABLE) {
626 if (v1.size() != v2.size())
627 return false;
628 for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2)
629 if (! ((std::isnan(*it1) && std::isnan(*it2)) || *it1 == *it2))
630 return false;
631 return true;
632 } else
633 // Not supporting nullable values, the default vector compare is cheaper.
634 return v1 == v2;
635 }
636
637 private:
638 friend class cereal::access;
serialize(Archive & ar)639 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<double>>(this)); }
640 };
641
642 using ConfigOptionFloats = ConfigOptionFloatsTempl<false>;
643 using ConfigOptionFloatsNullable = ConfigOptionFloatsTempl<true>;
644
645 class ConfigOptionInt : public ConfigOptionSingle<int>
646 {
647 public:
ConfigOptionInt()648 ConfigOptionInt() : ConfigOptionSingle<int>(0) {}
ConfigOptionInt(int value)649 explicit ConfigOptionInt(int value) : ConfigOptionSingle<int>(value) {}
ConfigOptionInt(double _value)650 explicit ConfigOptionInt(double _value) : ConfigOptionSingle<int>(int(floor(_value + 0.5))) {}
651
static_type()652 static ConfigOptionType static_type() { return coInt; }
type() const653 ConfigOptionType type() const override { return static_type(); }
getInt() const654 int getInt() const override { return this->value; }
setInt(int val)655 void setInt(int val) override { this->value = val; }
clone() const656 ConfigOption* clone() const override { return new ConfigOptionInt(*this); }
operator ==(const ConfigOptionInt & rhs) const657 bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; }
658
serialize() const659 std::string serialize() const override
660 {
661 std::ostringstream ss;
662 ss << this->value;
663 return ss.str();
664 }
665
deserialize(const std::string & str,bool append=false)666 bool deserialize(const std::string &str, bool append = false) override
667 {
668 UNUSED(append);
669 std::istringstream iss(str);
670 iss >> this->value;
671 return !iss.fail();
672 }
673
operator =(const ConfigOption * opt)674 ConfigOptionInt& operator=(const ConfigOption *opt)
675 {
676 this->set(opt);
677 return *this;
678 }
679
680 private:
681 friend class cereal::access;
serialize(Archive & ar)682 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<int>>(this)); }
683 };
684
685 template<bool NULLABLE>
686 class ConfigOptionIntsTempl : public ConfigOptionVector<int>
687 {
688 public:
ConfigOptionIntsTempl()689 ConfigOptionIntsTempl() : ConfigOptionVector<int>() {}
ConfigOptionIntsTempl(size_t n,int value)690 explicit ConfigOptionIntsTempl(size_t n, int value) : ConfigOptionVector<int>(n, value) {}
ConfigOptionIntsTempl(std::initializer_list<int> il)691 explicit ConfigOptionIntsTempl(std::initializer_list<int> il) : ConfigOptionVector<int>(std::move(il)) {}
692
static_type()693 static ConfigOptionType static_type() { return coInts; }
type() const694 ConfigOptionType type() const override { return static_type(); }
clone() const695 ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); }
operator =(const ConfigOption * opt)696 ConfigOptionIntsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionIntsTempl & rhs) const697 bool operator==(const ConfigOptionIntsTempl &rhs) const { return this->values == rhs.values; }
698 // Could a special "nil" value be stored inside the vector, indicating undefined value?
nullable() const699 bool nullable() const override { return NULLABLE; }
700 // Special "nil" value to be stored into the vector if this->supports_nil().
nil_value()701 static int nil_value() { return std::numeric_limits<int>::max(); }
702 // A scalar is nil, or all values of a vector are nil.
is_nil() const703 bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; }
is_nil(size_t idx) const704 bool is_nil(size_t idx) const override { return this->values[idx] == nil_value(); }
705
serialize() const706 std::string serialize() const override
707 {
708 std::ostringstream ss;
709 for (const int &v : this->values) {
710 if (&v != &this->values.front())
711 ss << ",";
712 serialize_single_value(ss, v);
713 }
714 return ss.str();
715 }
716
vserialize() const717 std::vector<std::string> vserialize() const override
718 {
719 std::vector<std::string> vv;
720 vv.reserve(this->values.size());
721 for (const int v : this->values) {
722 std::ostringstream ss;
723 serialize_single_value(ss, v);
724 vv.push_back(ss.str());
725 }
726 return vv;
727 }
728
deserialize(const std::string & str,bool append=false)729 bool deserialize(const std::string &str, bool append = false) override
730 {
731 if (! append)
732 this->values.clear();
733 std::istringstream is(str);
734 std::string item_str;
735 while (std::getline(is, item_str, ',')) {
736 boost::trim(item_str);
737 if (item_str == "nil") {
738 if (NULLABLE)
739 this->values.push_back(nil_value());
740 else
741 throw ConfigurationError("Deserializing nil into a non-nullable object");
742 } else {
743 std::istringstream iss(item_str);
744 int value;
745 iss >> value;
746 this->values.push_back(value);
747 }
748 }
749 return true;
750 }
751
752 private:
serialize_single_value(std::ostringstream & ss,const int v) const753 void serialize_single_value(std::ostringstream &ss, const int v) const {
754 if (v == nil_value()) {
755 if (NULLABLE)
756 ss << "nil";
757 else
758 throw ConfigurationError("Serializing NaN");
759 } else
760 ss << v;
761 }
762
763 friend class cereal::access;
serialize(Archive & ar)764 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<int>>(this)); }
765 };
766
767 using ConfigOptionInts = ConfigOptionIntsTempl<false>;
768 using ConfigOptionIntsNullable = ConfigOptionIntsTempl<true>;
769
770 class ConfigOptionString : public ConfigOptionSingle<std::string>
771 {
772 public:
ConfigOptionString()773 ConfigOptionString() : ConfigOptionSingle<std::string>("") {}
ConfigOptionString(const std::string & value)774 explicit ConfigOptionString(const std::string &value) : ConfigOptionSingle<std::string>(value) {}
775
static_type()776 static ConfigOptionType static_type() { return coString; }
type() const777 ConfigOptionType type() const override { return static_type(); }
clone() const778 ConfigOption* clone() const override { return new ConfigOptionString(*this); }
operator =(const ConfigOption * opt)779 ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionString & rhs) const780 bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; }
empty() const781 bool empty() const { return this->value.empty(); }
782
serialize() const783 std::string serialize() const override
784 {
785 return escape_string_cstyle(this->value);
786 }
787
deserialize(const std::string & str,bool append=false)788 bool deserialize(const std::string &str, bool append = false) override
789 {
790 UNUSED(append);
791 return unescape_string_cstyle(str, this->value);
792 }
793
794 private:
795 friend class cereal::access;
serialize(Archive & ar)796 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<std::string>>(this)); }
797 };
798
799 // semicolon-separated strings
800 class ConfigOptionStrings : public ConfigOptionVector<std::string>
801 {
802 public:
ConfigOptionStrings()803 ConfigOptionStrings() : ConfigOptionVector<std::string>() {}
ConfigOptionStrings(size_t n,const std::string & value)804 explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector<std::string>(n, value) {}
ConfigOptionStrings(const std::vector<std::string> & values)805 explicit ConfigOptionStrings(const std::vector<std::string> &values) : ConfigOptionVector<std::string>(values) {}
ConfigOptionStrings(std::vector<std::string> && values)806 explicit ConfigOptionStrings(std::vector<std::string> &&values) : ConfigOptionVector<std::string>(std::move(values)) {}
ConfigOptionStrings(std::initializer_list<std::string> il)807 explicit ConfigOptionStrings(std::initializer_list<std::string> il) : ConfigOptionVector<std::string>(std::move(il)) {}
808
static_type()809 static ConfigOptionType static_type() { return coStrings; }
type() const810 ConfigOptionType type() const override { return static_type(); }
clone() const811 ConfigOption* clone() const override { return new ConfigOptionStrings(*this); }
operator =(const ConfigOption * opt)812 ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionStrings & rhs) const813 bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; }
is_nil(size_t) const814 bool is_nil(size_t) const override { return false; }
815
serialize() const816 std::string serialize() const override
817 {
818 return escape_strings_cstyle(this->values);
819 }
820
vserialize() const821 std::vector<std::string> vserialize() const override
822 {
823 return this->values;
824 }
825
deserialize(const std::string & str,bool append=false)826 bool deserialize(const std::string &str, bool append = false) override
827 {
828 if (! append)
829 this->values.clear();
830 return unescape_strings_cstyle(str, this->values);
831 }
832
833 private:
834 friend class cereal::access;
serialize(Archive & ar)835 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<std::string>>(this)); }
836 };
837
838 class ConfigOptionPercent : public ConfigOptionFloat
839 {
840 public:
ConfigOptionPercent()841 ConfigOptionPercent() : ConfigOptionFloat(0) {}
ConfigOptionPercent(double _value)842 explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {}
843
static_type()844 static ConfigOptionType static_type() { return coPercent; }
type() const845 ConfigOptionType type() const override { return static_type(); }
clone() const846 ConfigOption* clone() const override { return new ConfigOptionPercent(*this); }
operator =(const ConfigOption * opt)847 ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionPercent & rhs) const848 bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; }
get_abs_value(double ratio_over) const849 double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; }
850
serialize() const851 std::string serialize() const override
852 {
853 std::ostringstream ss;
854 ss << this->value;
855 std::string s(ss.str());
856 s += "%";
857 return s;
858 }
859
deserialize(const std::string & str,bool append=false)860 bool deserialize(const std::string &str, bool append = false) override
861 {
862 UNUSED(append);
863 // don't try to parse the trailing % since it's optional
864 std::istringstream iss(str);
865 iss >> this->value;
866 return !iss.fail();
867 }
868
869 private:
870 friend class cereal::access;
serialize(Archive & ar)871 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloat>(this)); }
872 };
873
874 template<bool NULLABLE>
875 class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl<NULLABLE>
876 {
877 public:
ConfigOptionPercentsTempl()878 ConfigOptionPercentsTempl() : ConfigOptionFloatsTempl<NULLABLE>() {}
ConfigOptionPercentsTempl(size_t n,double value)879 explicit ConfigOptionPercentsTempl(size_t n, double value) : ConfigOptionFloatsTempl<NULLABLE>(n, value) {}
ConfigOptionPercentsTempl(std::initializer_list<double> il)880 explicit ConfigOptionPercentsTempl(std::initializer_list<double> il) : ConfigOptionFloatsTempl<NULLABLE>(std::move(il)) {}
ConfigOptionPercentsTempl(const std::vector<double> & vec)881 explicit ConfigOptionPercentsTempl(const std::vector<double>& vec) : ConfigOptionFloatsTempl<NULLABLE>(vec) {}
ConfigOptionPercentsTempl(std::vector<double> && vec)882 explicit ConfigOptionPercentsTempl(std::vector<double>&& vec) : ConfigOptionFloatsTempl<NULLABLE>(std::move(vec)) {}
883
static_type()884 static ConfigOptionType static_type() { return coPercents; }
type() const885 ConfigOptionType type() const override { return static_type(); }
clone() const886 ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); }
operator =(const ConfigOption * opt)887 ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionPercentsTempl & rhs) const888 bool operator==(const ConfigOptionPercentsTempl &rhs) const { return this->values == rhs.values; }
889
serialize() const890 std::string serialize() const override
891 {
892 std::ostringstream ss;
893 for (const double &v : this->values) {
894 if (&v != &this->values.front())
895 ss << ",";
896 this->serialize_single_value(ss, v);
897 if (! std::isnan(v))
898 ss << "%";
899 }
900 std::string str = ss.str();
901 return str;
902 }
903
vserialize() const904 std::vector<std::string> vserialize() const override
905 {
906 std::vector<std::string> vv;
907 vv.reserve(this->values.size());
908 for (const double v : this->values) {
909 std::ostringstream ss;
910 this->serialize_single_value(ss, v);
911 if (! std::isnan(v))
912 ss << "%";
913 vv.push_back(ss.str());
914 }
915 return vv;
916 }
917
918 // The float's deserialize function shall ignore the trailing optional %.
919 // bool deserialize(const std::string &str, bool append = false) override;
920
921 private:
922 friend class cereal::access;
serialize(Archive & ar)923 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloatsTempl<NULLABLE>>(this)); }
924 };
925
926 using ConfigOptionPercents = ConfigOptionPercentsTempl<false>;
927 using ConfigOptionPercentsNullable = ConfigOptionPercentsTempl<true>;
928
929 class ConfigOptionFloatOrPercent : public ConfigOptionPercent
930 {
931 public:
932 bool percent;
ConfigOptionFloatOrPercent()933 ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {}
ConfigOptionFloatOrPercent(double _value,bool _percent)934 explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {}
935
static_type()936 static ConfigOptionType static_type() { return coFloatOrPercent; }
type() const937 ConfigOptionType type() const override { return static_type(); }
clone() const938 ConfigOption* clone() const override { return new ConfigOptionFloatOrPercent(*this); }
operator =(const ConfigOption * opt)939 ConfigOptionFloatOrPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOption & rhs) const940 bool operator==(const ConfigOption &rhs) const override
941 {
942 if (rhs.type() != this->type())
943 throw ConfigurationError("ConfigOptionFloatOrPercent: Comparing incompatible types");
944 assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
945 return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
946 }
operator ==(const ConfigOptionFloatOrPercent & rhs) const947 bool operator==(const ConfigOptionFloatOrPercent &rhs) const
948 { return this->value == rhs.value && this->percent == rhs.percent; }
get_abs_value(double ratio_over) const949 double get_abs_value(double ratio_over) const
950 { return this->percent ? (ratio_over * this->value / 100) : this->value; }
951
set(const ConfigOption * rhs)952 void set(const ConfigOption *rhs) override {
953 if (rhs->type() != this->type())
954 throw ConfigurationError("ConfigOptionFloatOrPercent: Assigning an incompatible type");
955 assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs));
956 *this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs);
957 }
958
serialize() const959 std::string serialize() const override
960 {
961 std::ostringstream ss;
962 ss << this->value;
963 std::string s(ss.str());
964 if (this->percent) s += "%";
965 return s;
966 }
967
deserialize(const std::string & str,bool append=false)968 bool deserialize(const std::string &str, bool append = false) override
969 {
970 UNUSED(append);
971 this->percent = str.find_first_of("%") != std::string::npos;
972 std::istringstream iss(str);
973 iss >> this->value;
974 return !iss.fail();
975 }
976
977 private:
978 friend class cereal::access;
serialize(Archive & ar)979 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
980 };
981
982
983 struct FloatOrPercent
984 {
985 double value;
986 bool percent;
987
988 private:
989 friend class cereal::access;
serializeSlic3r::FloatOrPercent990 template<class Archive> void serialize(Archive & ar) { ar(this->value); ar(this->percent); }
991 };
992
operator ==(const FloatOrPercent & l,const FloatOrPercent & r)993 inline bool operator==(const FloatOrPercent &l, const FloatOrPercent &r)
994 {
995 return l.value == r.value && l.percent == r.percent;
996 }
997
operator !=(const FloatOrPercent & l,const FloatOrPercent & r)998 inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r)
999 {
1000 return !(l == r);
1001 }
1002
1003 template<bool NULLABLE>
1004 class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent>
1005 {
1006 public:
ConfigOptionFloatsOrPercentsTempl()1007 ConfigOptionFloatsOrPercentsTempl() : ConfigOptionVector<FloatOrPercent>() {}
ConfigOptionFloatsOrPercentsTempl(size_t n,FloatOrPercent value)1008 explicit ConfigOptionFloatsOrPercentsTempl(size_t n, FloatOrPercent value) : ConfigOptionVector<FloatOrPercent>(n, value) {}
ConfigOptionFloatsOrPercentsTempl(std::initializer_list<FloatOrPercent> il)1009 explicit ConfigOptionFloatsOrPercentsTempl(std::initializer_list<FloatOrPercent> il) : ConfigOptionVector<FloatOrPercent>(std::move(il)) {}
ConfigOptionFloatsOrPercentsTempl(const std::vector<FloatOrPercent> & vec)1010 explicit ConfigOptionFloatsOrPercentsTempl(const std::vector<FloatOrPercent> &vec) : ConfigOptionVector<FloatOrPercent>(vec) {}
ConfigOptionFloatsOrPercentsTempl(std::vector<FloatOrPercent> && vec)1011 explicit ConfigOptionFloatsOrPercentsTempl(std::vector<FloatOrPercent> &&vec) : ConfigOptionVector<FloatOrPercent>(std::move(vec)) {}
1012
static_type()1013 static ConfigOptionType static_type() { return coFloatsOrPercents; }
type() const1014 ConfigOptionType type() const override { return static_type(); }
clone() const1015 ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); }
operator ==(const ConfigOptionFloatsOrPercentsTempl & rhs) const1016 bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
operator ==(const ConfigOption & rhs) const1017 bool operator==(const ConfigOption &rhs) const override {
1018 if (rhs.type() != this->type())
1019 throw ConfigurationError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
1020 assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
1021 return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
1022 }
1023 // Could a special "nil" value be stored inside the vector, indicating undefined value?
nullable() const1024 bool nullable() const override { return NULLABLE; }
1025 // Special "nil" value to be stored into the vector if this->supports_nil().
nil_value()1026 static FloatOrPercent nil_value() { return { std::numeric_limits<double>::quiet_NaN(), false }; }
1027 // A scalar is nil, or all values of a vector are nil.
is_nil() const1028 bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v.value)) return false; return true; }
is_nil(size_t idx) const1029 bool is_nil(size_t idx) const override { return std::isnan(this->values[idx].value); }
1030
serialize() const1031 std::string serialize() const override
1032 {
1033 std::ostringstream ss;
1034 for (const FloatOrPercent &v : this->values) {
1035 if (&v != &this->values.front())
1036 ss << ",";
1037 serialize_single_value(ss, v);
1038 }
1039 return ss.str();
1040 }
1041
vserialize() const1042 std::vector<std::string> vserialize() const override
1043 {
1044 std::vector<std::string> vv;
1045 vv.reserve(this->values.size());
1046 for (const FloatOrPercent &v : this->values) {
1047 std::ostringstream ss;
1048 serialize_single_value(ss, v);
1049 vv.push_back(ss.str());
1050 }
1051 return vv;
1052 }
1053
deserialize(const std::string & str,bool append=false)1054 bool deserialize(const std::string &str, bool append = false) override
1055 {
1056 if (! append)
1057 this->values.clear();
1058 std::istringstream is(str);
1059 std::string item_str;
1060 while (std::getline(is, item_str, ',')) {
1061 boost::trim(item_str);
1062 if (item_str == "nil") {
1063 if (NULLABLE)
1064 this->values.push_back(nil_value());
1065 else
1066 throw ConfigurationError("Deserializing nil into a non-nullable object");
1067 } else {
1068 bool percent = item_str.find_first_of("%") != std::string::npos;
1069 std::istringstream iss(item_str);
1070 double value;
1071 iss >> value;
1072 this->values.push_back({ value, percent });
1073 }
1074 }
1075 return true;
1076 }
1077
operator =(const ConfigOption * opt)1078 ConfigOptionFloatsOrPercentsTempl& operator=(const ConfigOption *opt)
1079 {
1080 this->set(opt);
1081 return *this;
1082 }
1083
1084 protected:
serialize_single_value(std::ostringstream & ss,const FloatOrPercent & v) const1085 void serialize_single_value(std::ostringstream &ss, const FloatOrPercent &v) const {
1086 if (std::isfinite(v.value)) {
1087 ss << v.value;
1088 if (v.percent)
1089 ss << "%";
1090 } else if (std::isnan(v.value)) {
1091 if (NULLABLE)
1092 ss << "nil";
1093 else
1094 throw ConfigurationError("Serializing NaN");
1095 } else
1096 throw ConfigurationError("Serializing invalid number");
1097 }
vectors_equal(const std::vector<FloatOrPercent> & v1,const std::vector<FloatOrPercent> & v2)1098 static bool vectors_equal(const std::vector<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) {
1099 if (NULLABLE) {
1100 if (v1.size() != v2.size())
1101 return false;
1102 for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2)
1103 if (! ((std::isnan(it1->value) && std::isnan(it2->value)) || *it1 == *it2))
1104 return false;
1105 return true;
1106 } else
1107 // Not supporting nullable values, the default vector compare is cheaper.
1108 return v1 == v2;
1109 }
1110
1111 private:
1112 friend class cereal::access;
serialize(Archive & ar)1113 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<FloatOrPercent>>(this)); }
1114 };
1115
1116 using ConfigOptionFloatsOrPercents = ConfigOptionFloatsOrPercentsTempl<false>;
1117 using ConfigOptionFloatsOrPercentsNullable = ConfigOptionFloatsOrPercentsTempl<true>;
1118
1119 class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
1120 {
1121 public:
ConfigOptionPoint()1122 ConfigOptionPoint() : ConfigOptionSingle<Vec2d>(Vec2d(0,0)) {}
ConfigOptionPoint(const Vec2d & value)1123 explicit ConfigOptionPoint(const Vec2d &value) : ConfigOptionSingle<Vec2d>(value) {}
1124
static_type()1125 static ConfigOptionType static_type() { return coPoint; }
type() const1126 ConfigOptionType type() const override { return static_type(); }
clone() const1127 ConfigOption* clone() const override { return new ConfigOptionPoint(*this); }
operator =(const ConfigOption * opt)1128 ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionPoint & rhs) const1129 bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; }
1130
serialize() const1131 std::string serialize() const override
1132 {
1133 std::ostringstream ss;
1134 ss << this->value(0);
1135 ss << ",";
1136 ss << this->value(1);
1137 return ss.str();
1138 }
1139
deserialize(const std::string & str,bool append=false)1140 bool deserialize(const std::string &str, bool append = false) override
1141 {
1142 UNUSED(append);
1143 char dummy;
1144 return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 ||
1145 sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2;
1146 }
1147
1148 private:
1149 friend class cereal::access;
serialize(Archive & ar)1150 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec2d>>(this)); }
1151 };
1152
1153 class ConfigOptionPoints : public ConfigOptionVector<Vec2d>
1154 {
1155 public:
ConfigOptionPoints()1156 ConfigOptionPoints() : ConfigOptionVector<Vec2d>() {}
ConfigOptionPoints(size_t n,const Vec2d & value)1157 explicit ConfigOptionPoints(size_t n, const Vec2d &value) : ConfigOptionVector<Vec2d>(n, value) {}
ConfigOptionPoints(std::initializer_list<Vec2d> il)1158 explicit ConfigOptionPoints(std::initializer_list<Vec2d> il) : ConfigOptionVector<Vec2d>(std::move(il)) {}
ConfigOptionPoints(const std::vector<Vec2d> & values)1159 explicit ConfigOptionPoints(const std::vector<Vec2d> &values) : ConfigOptionVector<Vec2d>(values) {}
1160
static_type()1161 static ConfigOptionType static_type() { return coPoints; }
type() const1162 ConfigOptionType type() const override { return static_type(); }
clone() const1163 ConfigOption* clone() const override { return new ConfigOptionPoints(*this); }
operator =(const ConfigOption * opt)1164 ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionPoints & rhs) const1165 bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; }
is_nil(size_t) const1166 bool is_nil(size_t) const override { return false; }
1167
serialize() const1168 std::string serialize() const override
1169 {
1170 std::ostringstream ss;
1171 for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
1172 if (it - this->values.begin() != 0) ss << ",";
1173 ss << (*it)(0);
1174 ss << "x";
1175 ss << (*it)(1);
1176 }
1177 return ss.str();
1178 }
1179
vserialize() const1180 std::vector<std::string> vserialize() const override
1181 {
1182 std::vector<std::string> vv;
1183 for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
1184 std::ostringstream ss;
1185 ss << *it;
1186 vv.push_back(ss.str());
1187 }
1188 return vv;
1189 }
1190
deserialize(const std::string & str,bool append=false)1191 bool deserialize(const std::string &str, bool append = false) override
1192 {
1193 if (! append)
1194 this->values.clear();
1195 std::istringstream is(str);
1196 std::string point_str;
1197 while (std::getline(is, point_str, ',')) {
1198 Vec2d point(Vec2d::Zero());
1199 std::istringstream iss(point_str);
1200 std::string coord_str;
1201 if (std::getline(iss, coord_str, 'x')) {
1202 std::istringstream(coord_str) >> point(0);
1203 if (std::getline(iss, coord_str, 'x')) {
1204 std::istringstream(coord_str) >> point(1);
1205 }
1206 }
1207 this->values.push_back(point);
1208 }
1209 return true;
1210 }
1211
1212 private:
1213 friend class cereal::access;
save(Archive & archive) const1214 template<class Archive> void save(Archive& archive) const {
1215 size_t cnt = this->values.size();
1216 archive(cnt);
1217 archive.saveBinary((const char*)this->values.data(), sizeof(Vec2d) * cnt);
1218 }
load(Archive & archive)1219 template<class Archive> void load(Archive& archive) {
1220 size_t cnt;
1221 archive(cnt);
1222 this->values.assign(cnt, Vec2d());
1223 archive.loadBinary((char*)this->values.data(), sizeof(Vec2d) * cnt);
1224 }
1225 };
1226
1227 class ConfigOptionPoint3 : public ConfigOptionSingle<Vec3d>
1228 {
1229 public:
ConfigOptionPoint3()1230 ConfigOptionPoint3() : ConfigOptionSingle<Vec3d>(Vec3d(0,0,0)) {}
ConfigOptionPoint3(const Vec3d & value)1231 explicit ConfigOptionPoint3(const Vec3d &value) : ConfigOptionSingle<Vec3d>(value) {}
1232
static_type()1233 static ConfigOptionType static_type() { return coPoint3; }
type() const1234 ConfigOptionType type() const override { return static_type(); }
clone() const1235 ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); }
operator =(const ConfigOption * opt)1236 ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionPoint3 & rhs) const1237 bool operator==(const ConfigOptionPoint3 &rhs) const { return this->value == rhs.value; }
1238
serialize() const1239 std::string serialize() const override
1240 {
1241 std::ostringstream ss;
1242 ss << this->value(0);
1243 ss << ",";
1244 ss << this->value(1);
1245 ss << ",";
1246 ss << this->value(2);
1247 return ss.str();
1248 }
1249
deserialize(const std::string & str,bool append=false)1250 bool deserialize(const std::string &str, bool append = false) override
1251 {
1252 UNUSED(append);
1253 char dummy;
1254 return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 ||
1255 sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2;
1256 }
1257
1258 private:
1259 friend class cereal::access;
serialize(Archive & ar)1260 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec3d>>(this)); }
1261 };
1262
1263 class ConfigOptionBool : public ConfigOptionSingle<bool>
1264 {
1265 public:
ConfigOptionBool()1266 ConfigOptionBool() : ConfigOptionSingle<bool>(false) {}
ConfigOptionBool(bool _value)1267 explicit ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {}
1268
static_type()1269 static ConfigOptionType static_type() { return coBool; }
type() const1270 ConfigOptionType type() const override { return static_type(); }
getBool() const1271 bool getBool() const override { return this->value; }
clone() const1272 ConfigOption* clone() const override { return new ConfigOptionBool(*this); }
operator =(const ConfigOption * opt)1273 ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionBool & rhs) const1274 bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; }
1275
serialize() const1276 std::string serialize() const override
1277 {
1278 return std::string(this->value ? "1" : "0");
1279 }
1280
deserialize(const std::string & str,bool append=false)1281 bool deserialize(const std::string &str, bool append = false) override
1282 {
1283 UNUSED(append);
1284 if (str == "1") {
1285 this->value = true;
1286 return true;
1287 }
1288 if (str == "0") {
1289 this->value = false;
1290 return true;
1291 }
1292 return false;
1293 }
1294
1295 private:
1296 friend class cereal::access;
serialize(Archive & ar)1297 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<bool>>(this)); }
1298 };
1299
1300 template<bool NULLABLE>
1301 class ConfigOptionBoolsTempl : public ConfigOptionVector<unsigned char>
1302 {
1303 public:
ConfigOptionBoolsTempl()1304 ConfigOptionBoolsTempl() : ConfigOptionVector<unsigned char>() {}
ConfigOptionBoolsTempl(size_t n,bool value)1305 explicit ConfigOptionBoolsTempl(size_t n, bool value) : ConfigOptionVector<unsigned char>(n, (unsigned char)value) {}
ConfigOptionBoolsTempl(std::initializer_list<bool> il)1306 explicit ConfigOptionBoolsTempl(std::initializer_list<bool> il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); }
ConfigOptionBoolsTempl(std::initializer_list<unsigned char> il)1307 explicit ConfigOptionBoolsTempl(std::initializer_list<unsigned char> il) { values.reserve(il.size()); for (unsigned char b : il) values.emplace_back(b); }
ConfigOptionBoolsTempl(const std::vector<unsigned char> & vec)1308 explicit ConfigOptionBoolsTempl(const std::vector<unsigned char>& vec) : ConfigOptionVector<unsigned char>(vec) {}
ConfigOptionBoolsTempl(std::vector<unsigned char> && vec)1309 explicit ConfigOptionBoolsTempl(std::vector<unsigned char>&& vec) : ConfigOptionVector<unsigned char>(std::move(vec)) {}
1310
static_type()1311 static ConfigOptionType static_type() { return coBools; }
type() const1312 ConfigOptionType type() const override { return static_type(); }
clone() const1313 ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); }
operator =(const ConfigOption * opt)1314 ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionBoolsTempl & rhs) const1315 bool operator==(const ConfigOptionBoolsTempl &rhs) const { return this->values == rhs.values; }
1316 // Could a special "nil" value be stored inside the vector, indicating undefined value?
nullable() const1317 bool nullable() const override { return NULLABLE; }
1318 // Special "nil" value to be stored into the vector if this->supports_nil().
nil_value()1319 static unsigned char nil_value() { return std::numeric_limits<unsigned char>::max(); }
1320 // A scalar is nil, or all values of a vector are nil.
is_nil() const1321 bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; }
is_nil(size_t idx) const1322 bool is_nil(size_t idx) const override { return this->values[idx] == nil_value(); }
1323
get_at(size_t i)1324 bool& get_at(size_t i) {
1325 assert(! this->values.empty());
1326 return *reinterpret_cast<bool*>(&((i < this->values.size()) ? this->values[i] : this->values.front()));
1327 }
1328
1329 //FIXME this smells, the parent class has the method declared returning (unsigned char&).
get_at(size_t i) const1330 bool get_at(size_t i) const { return ((i < this->values.size()) ? this->values[i] : this->values.front()) != 0; }
1331
serialize() const1332 std::string serialize() const override
1333 {
1334 std::ostringstream ss;
1335 for (const unsigned char &v : this->values) {
1336 if (&v != &this->values.front())
1337 ss << ",";
1338 this->serialize_single_value(ss, v);
1339 }
1340 return ss.str();
1341 }
1342
vserialize() const1343 std::vector<std::string> vserialize() const override
1344 {
1345 std::vector<std::string> vv;
1346 for (const unsigned char v : this->values) {
1347 std::ostringstream ss;
1348 this->serialize_single_value(ss, v);
1349 vv.push_back(ss.str());
1350 }
1351 return vv;
1352 }
1353
deserialize_with_substitutions(const std::string & str,bool append,ConfigHelpers::DeserializationSubstitution substitution)1354 ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, ConfigHelpers::DeserializationSubstitution substitution)
1355 {
1356 if (! append)
1357 this->values.clear();
1358 std::istringstream is(str);
1359 std::string item_str;
1360 bool substituted = false;
1361 while (std::getline(is, item_str, ',')) {
1362 boost::trim(item_str);
1363 unsigned char new_value = 0;
1364 if (item_str == "nil") {
1365 if (NULLABLE)
1366 new_value = nil_value();
1367 else
1368 throw ConfigurationError("Deserializing nil into a non-nullable object");
1369 } else if (item_str == "1") {
1370 new_value = true;
1371 } else if (item_str == "0") {
1372 new_value = false;
1373 } else if (substitution != ConfigHelpers::DeserializationSubstitution::Disabled && ConfigHelpers::looks_like_enum_value(item_str)) {
1374 new_value = ConfigHelpers::enum_looks_like_true_value(item_str) || substitution == ConfigHelpers::DeserializationSubstitution::DefaultsToTrue;
1375 substituted = true;
1376 } else
1377 return ConfigHelpers::DeserializationResult::Failed;
1378 this->values.push_back(new_value);
1379 }
1380 return substituted ? ConfigHelpers::DeserializationResult::Substituted : ConfigHelpers::DeserializationResult::Loaded;
1381 }
1382
deserialize(const std::string & str,bool append=false)1383 bool deserialize(const std::string &str, bool append = false) override
1384 {
1385 return this->deserialize_with_substitutions(str, append, ConfigHelpers::DeserializationSubstitution::Disabled) == ConfigHelpers::DeserializationResult::Loaded;
1386 }
1387
1388 protected:
serialize_single_value(std::ostringstream & ss,const unsigned char v) const1389 void serialize_single_value(std::ostringstream &ss, const unsigned char v) const {
1390 if (v == nil_value()) {
1391 if (NULLABLE)
1392 ss << "nil";
1393 else
1394 throw ConfigurationError("Serializing NaN");
1395 } else
1396 ss << (v ? "1" : "0");
1397 }
1398
1399 private:
1400 friend class cereal::access;
serialize(Archive & ar)1401 template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<unsigned char>>(this)); }
1402 };
1403
1404 using ConfigOptionBools = ConfigOptionBoolsTempl<false>;
1405 using ConfigOptionBoolsNullable = ConfigOptionBoolsTempl<true>;
1406
1407 // Map from an enum integer value to an enum name.
1408 typedef std::vector<std::string> t_config_enum_names;
1409 // Map from an enum name to an enum integer value.
1410 typedef std::map<std::string,int> t_config_enum_values;
1411
1412 template <class T>
1413 class ConfigOptionEnum : public ConfigOptionSingle<T>
1414 {
1415 public:
1416 // by default, use the first value (0) of the T enum type
ConfigOptionEnum()1417 ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {}
ConfigOptionEnum(T _value)1418 explicit ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {}
1419
static_type()1420 static ConfigOptionType static_type() { return coEnum; }
type() const1421 ConfigOptionType type() const override { return static_type(); }
clone() const1422 ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); }
operator =(const ConfigOption * opt)1423 ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionEnum<T> & rhs) const1424 bool operator==(const ConfigOptionEnum<T> &rhs) const { return this->value == rhs.value; }
getInt() const1425 int getInt() const override { return (int)this->value; }
1426
operator ==(const ConfigOption & rhs) const1427 bool operator==(const ConfigOption &rhs) const override
1428 {
1429 if (rhs.type() != this->type())
1430 throw ConfigurationError("ConfigOptionEnum<T>: Comparing incompatible types");
1431 // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
1432 return this->value == (T)rhs.getInt();
1433 }
1434
set(const ConfigOption * rhs)1435 void set(const ConfigOption *rhs) override {
1436 if (rhs->type() != this->type())
1437 throw ConfigurationError("ConfigOptionEnum<T>: Assigning an incompatible type");
1438 // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
1439 this->value = (T)rhs->getInt();
1440 }
1441
serialize() const1442 std::string serialize() const override
1443 {
1444 const t_config_enum_names& names = ConfigOptionEnum<T>::get_enum_names();
1445 assert(static_cast<int>(this->value) < int(names.size()));
1446 return names[static_cast<int>(this->value)];
1447 }
1448
deserialize(const std::string & str,bool append=false)1449 bool deserialize(const std::string &str, bool append = false) override
1450 {
1451 UNUSED(append);
1452 return from_string(str, this->value);
1453 }
1454
has(T value)1455 static bool has(T value)
1456 {
1457 for (const std::pair<std::string, int> &kvp : ConfigOptionEnum<T>::get_enum_values())
1458 if (kvp.second == value)
1459 return true;
1460 return false;
1461 }
1462
1463 // Map from an enum name to an enum integer value.
get_enum_names()1464 static const t_config_enum_names& get_enum_names()
1465 {
1466 static t_config_enum_names names;
1467 if (names.empty()) {
1468 // Initialize the map.
1469 const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
1470 int cnt = 0;
1471 for (const std::pair<std::string, int> &kvp : enum_keys_map)
1472 cnt = std::max(cnt, kvp.second);
1473 cnt += 1;
1474 names.assign(cnt, "");
1475 for (const std::pair<std::string, int> &kvp : enum_keys_map)
1476 names[kvp.second] = kvp.first;
1477 }
1478 return names;
1479 }
1480 // Map from an enum name to an enum integer value.
1481 static const t_config_enum_values& get_enum_values();
1482
from_string(const std::string & str,T & value)1483 static bool from_string(const std::string &str, T &value)
1484 {
1485 const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
1486 auto it = enum_keys_map.find(str);
1487 if (it == enum_keys_map.end())
1488 return false;
1489 value = static_cast<T>(it->second);
1490 return true;
1491 }
1492 };
1493
1494 // Generic enum configuration value.
1495 // We use this one in DynamicConfig objects when creating a config value object for ConfigOptionType == coEnum.
1496 // In the StaticConfig, it is better to use the specialized ConfigOptionEnum<T> containers.
1497 class ConfigOptionEnumGeneric : public ConfigOptionInt
1498 {
1499 public:
ConfigOptionEnumGeneric(const t_config_enum_values * keys_map=nullptr)1500 ConfigOptionEnumGeneric(const t_config_enum_values* keys_map = nullptr) : keys_map(keys_map) {}
ConfigOptionEnumGeneric(const t_config_enum_values * keys_map,int value)1501 explicit ConfigOptionEnumGeneric(const t_config_enum_values* keys_map, int value) : ConfigOptionInt(value), keys_map(keys_map) {}
1502
1503 const t_config_enum_values* keys_map;
1504
static_type()1505 static ConfigOptionType static_type() { return coEnum; }
type() const1506 ConfigOptionType type() const override { return static_type(); }
clone() const1507 ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); }
operator =(const ConfigOption * opt)1508 ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
operator ==(const ConfigOptionEnumGeneric & rhs) const1509 bool operator==(const ConfigOptionEnumGeneric &rhs) const { return this->value == rhs.value; }
1510
operator ==(const ConfigOption & rhs) const1511 bool operator==(const ConfigOption &rhs) const override
1512 {
1513 if (rhs.type() != this->type())
1514 throw ConfigurationError("ConfigOptionEnumGeneric: Comparing incompatible types");
1515 // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
1516 return this->value == rhs.getInt();
1517 }
1518
set(const ConfigOption * rhs)1519 void set(const ConfigOption *rhs) override {
1520 if (rhs->type() != this->type())
1521 throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
1522 // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
1523 this->value = rhs->getInt();
1524 }
1525
serialize() const1526 std::string serialize() const override
1527 {
1528 for (const auto &kvp : *this->keys_map)
1529 if (kvp.second == this->value)
1530 return kvp.first;
1531 return std::string();
1532 }
1533
deserialize(const std::string & str,bool append=false)1534 bool deserialize(const std::string &str, bool append = false) override
1535 {
1536 UNUSED(append);
1537 auto it = this->keys_map->find(str);
1538 if (it == this->keys_map->end())
1539 return false;
1540 this->value = it->second;
1541 return true;
1542 }
1543
1544 private:
1545 friend class cereal::access;
serialize(Archive & ar)1546 template<class Archive> void serialize(Archive& ar) { ar(cereal::base_class<ConfigOptionInt>(this)); }
1547 };
1548
1549 // Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
1550 class ConfigOptionDef
1551 {
1552 public:
1553 // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map.
1554 t_config_option_key opt_key;
1555 // What type? bool, int, string etc.
1556 ConfigOptionType type = coNone;
1557 // If a type is nullable, then it accepts a "nil" value (scalar) or "nil" values (vector).
1558 bool nullable = false;
1559 // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
1560 Slic3r::clonable_ptr<const ConfigOption> default_value;
set_default_value(const ConfigOption * ptr)1561 void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
get_default_value() const1562 template<typename T> const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
1563
1564 // Create an empty option to be used as a base for deserialization of DynamicConfig.
1565 ConfigOption* create_empty_option() const;
1566 // Create a default option to be inserted into a DynamicConfig.
1567 ConfigOption* create_default_option() const;
1568
load_option_from_archive(Archive & archive) const1569 template<class Archive> ConfigOption* load_option_from_archive(Archive &archive) const {
1570 if (this->nullable) {
1571 switch (this->type) {
1572 case coFloats: { auto opt = new ConfigOptionFloatsNullable(); archive(*opt); return opt; }
1573 case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
1574 case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
1575 case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
1576 default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
1577 }
1578 } else {
1579 switch (this->type) {
1580 case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; }
1581 case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; }
1582 case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; }
1583 case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; }
1584 case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; }
1585 case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; }
1586 case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; }
1587 case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; }
1588 case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; }
1589 case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; }
1590 case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; }
1591 case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; }
1592 case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
1593 case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
1594 case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
1595 default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
1596 }
1597 }
1598 }
1599
save_option_to_archive(Archive & archive,const ConfigOption * opt) const1600 template<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const {
1601 if (this->nullable) {
1602 switch (this->type) {
1603 case coFloats: archive(*static_cast<const ConfigOptionFloatsNullable*>(opt)); break;
1604 case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
1605 case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
1606 case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
1607 default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
1608 }
1609 } else {
1610 switch (this->type) {
1611 case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
1612 case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
1613 case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
1614 case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
1615 case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
1616 case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
1617 case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
1618 case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
1619 case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
1620 case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
1621 case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
1622 case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
1623 case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
1624 case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
1625 case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
1626 default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
1627 }
1628 }
1629 // Make the compiler happy, shut up the warnings.
1630 return nullptr;
1631 }
1632
1633 // Usually empty.
1634 // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
1635 // "select_open" - to open a selection dialog (currently only a serial port selection).
1636 std::string gui_type;
1637 // Usually empty. Otherwise "serialized" or "show_value"
1638 // The flags may be combined.
1639 // "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon.
1640 // "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label.
1641 std::string gui_flags;
1642 // Label of the GUI input field.
1643 // In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value,
1644 // while full_label contains a label of a stand-alone field.
1645 // The full label is shown, when adding an override parameter for an object or a modified object.
1646 std::string label;
1647 std::string full_label;
1648 // With which printer technology is this configuration valid?
1649 PrinterTechnology printer_technology = ptUnknown;
1650 // Category of a configuration field, from the GUI perspective.
1651 // One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
1652 std::string category;
1653 // A tooltip text shown in the GUI.
1654 std::string tooltip;
1655 // Text right from the input field, usually a unit of measurement.
1656 std::string sidetext;
1657 // Format of this parameter on a command line.
1658 std::string cli;
1659 // Set for type == coFloatOrPercent.
1660 // It provides a link to a configuration value, of which this option provides a ratio.
1661 // For example,
1662 // For example external_perimeter_speed may be defined as a fraction of perimeter_speed.
1663 t_config_option_key ratio_over;
1664 // True for multiline strings.
1665 bool multiline = false;
1666 // For text input: If true, the GUI text box spans the complete page width.
1667 bool full_width = false;
1668 // For text input: If true, the GUI formats text as code (fixed-width)
1669 bool is_code = false;
1670 // Not editable. Currently only used for the display of the number of threads.
1671 bool readonly = false;
1672 // Height of a multiline GUI text box.
1673 int height = -1;
1674 // Optional width of an input field.
1675 int width = -1;
1676 // <min, max> limit of a numeric input.
1677 // If not set, the <min, max> is set to <INT_MIN, INT_MAX>
1678 // By setting min=0, only nonnegative input is allowed.
1679 int min = INT_MIN;
1680 int max = INT_MAX;
1681 ConfigOptionMode mode = comSimple;
1682 // Legacy names for this configuration option.
1683 // Used when parsing legacy configuration file.
1684 std::vector<t_config_option_key> aliases;
1685 // Sometimes a single value may well define multiple values in a "beginner" mode.
1686 // Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers".
1687 std::vector<t_config_option_key> shortcut;
1688 // Definition of values / labels for a combo box.
1689 // Mostly used for enums (when type == coEnum), but may be used for ints resp. floats, if gui_type is set to "i_enum_open" resp. "f_enum_open".
1690 std::vector<std::string> enum_values;
1691 std::vector<std::string> enum_labels;
1692 // For enums (when type == coEnum). Maps enum_values to enums.
1693 // Initialized by ConfigOptionEnum<xxx>::get_enum_values()
1694 const t_config_enum_values *enum_keys_map = nullptr;
1695
has_enum_value(const std::string & value) const1696 bool has_enum_value(const std::string &value) const {
1697 for (const std::string &v : enum_values)
1698 if (v == value)
1699 return true;
1700 return false;
1701 }
1702
1703 // 0 is an invalid key.
1704 size_t serialization_key_ordinal = 0;
1705
1706 // Returns the alternative CLI arguments for the given option.
1707 // If there are no cli arguments defined, use the key and replace underscores with dashes.
1708 std::vector<std::string> cli_args(const std::string &key) const;
1709
1710 // Assign this key to cli to disable CLI for this option.
1711 static const constexpr char *nocli = "~~~noCLI";
1712 };
1713
operator <(const ConfigSubstitution & lhs,const ConfigSubstitution & rhs)1714 inline bool operator<(const ConfigSubstitution &lhs, const ConfigSubstitution &rhs) throw() {
1715 return lhs.opt_def->opt_key < rhs.opt_def->opt_key ||
1716 (lhs.opt_def->opt_key == rhs.opt_def->opt_key && lhs.old_value < rhs.old_value);
1717 }
operator ==(const ConfigSubstitution & lhs,const ConfigSubstitution & rhs)1718 inline bool operator==(const ConfigSubstitution &lhs, const ConfigSubstitution &rhs) throw() {
1719 return lhs.opt_def == rhs.opt_def && lhs.old_value == rhs.old_value;
1720 }
1721
1722 // Map from a config option name to its definition.
1723 // The definition does not carry an actual value of the config option, only its constant default value.
1724 // t_config_option_key is std::string
1725 typedef std::map<t_config_option_key, ConfigOptionDef> t_optiondef_map;
1726
1727 // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
1728 // The configuration definition is static: It does not carry the actual configuration values,
1729 // but it carries the defaults of the configuration values.
1730 class ConfigDef
1731 {
1732 public:
1733 t_optiondef_map options;
1734 std::map<size_t, const ConfigOptionDef*> by_serialization_key_ordinal;
1735
has(const t_config_option_key & opt_key) const1736 bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; }
get(const t_config_option_key & opt_key) const1737 const ConfigOptionDef* get(const t_config_option_key &opt_key) const {
1738 t_optiondef_map::iterator it = const_cast<ConfigDef*>(this)->options.find(opt_key);
1739 return (it == this->options.end()) ? nullptr : &it->second;
1740 }
keys() const1741 std::vector<std::string> keys() const {
1742 std::vector<std::string> out;
1743 out.reserve(options.size());
1744 for(auto const& kvp : options)
1745 out.push_back(kvp.first);
1746 return out;
1747 }
1748
1749 // Iterate through all of the CLI options and write them to a stream.
1750 std::ostream& print_cli_help(
1751 std::ostream& out, bool show_defaults,
__anon78c802780102(const ConfigOptionDef &)1752 std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
1753
1754 protected:
1755 ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
1756 ConfigOptionDef* add_nullable(const t_config_option_key &opt_key, ConfigOptionType type);
1757 };
1758
1759 // A pure interface to resolving ConfigOptions.
1760 // This pure interface is useful as a base of ConfigBase, also it may be overriden to combine
1761 // various config sources.
1762 class ConfigOptionResolver
1763 {
1764 public:
ConfigOptionResolver()1765 ConfigOptionResolver() {}
~ConfigOptionResolver()1766 virtual ~ConfigOptionResolver() {}
1767
1768 // Find a ConfigOption instance for a given name.
1769 virtual const ConfigOption* optptr(const t_config_option_key &opt_key) const = 0;
1770
has(const t_config_option_key & opt_key) const1771 bool has(const t_config_option_key &opt_key) const { return this->optptr(opt_key) != nullptr; }
1772
option(const t_config_option_key & opt_key) const1773 const ConfigOption* option(const t_config_option_key &opt_key) const { return this->optptr(opt_key); }
1774
1775 template<typename TYPE>
option(const t_config_option_key & opt_key) const1776 const TYPE* option(const t_config_option_key& opt_key) const
1777 {
1778 const ConfigOption* opt = this->optptr(opt_key);
1779 return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<const TYPE*>(opt);
1780 }
1781
option_throw(const t_config_option_key & opt_key) const1782 const ConfigOption* option_throw(const t_config_option_key& opt_key) const
1783 {
1784 const ConfigOption* opt = this->optptr(opt_key);
1785 if (opt == nullptr)
1786 throw UnknownOptionException(opt_key);
1787 return opt;
1788 }
1789
1790 template<typename TYPE>
option_throw(const t_config_option_key & opt_key) const1791 const TYPE* option_throw(const t_config_option_key& opt_key) const
1792 {
1793 const ConfigOption* opt = this->option_throw(opt_key);
1794 if (opt->type() != TYPE::static_type())
1795 throw BadOptionTypeException("Conversion to a wrong type");
1796 return static_cast<TYPE*>(opt);
1797 }
1798 };
1799
1800
1801
1802 // An abstract configuration store.
1803 class ConfigBase : public ConfigOptionResolver
1804 {
1805 public:
1806 // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
1807 // The configuration definition is static: It does not carry the actual configuration values,
1808 // but it carries the defaults of the configuration values.
1809
ConfigBase()1810 ConfigBase() {}
~ConfigBase()1811 ~ConfigBase() override {}
1812
1813 // Virtual overridables:
1814 public:
1815 // Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
1816 virtual const ConfigDef* def() const = 0;
1817 // Find ando/or create a ConfigOption instance for a given name.
1818 virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
1819 // Collect names of all configuration values maintained by this configuration store.
1820 virtual t_config_option_keys keys() const = 0;
1821
1822 protected:
1823 // Verify whether the opt_key has not been obsoleted or renamed.
1824 // Both opt_key and value may be modified by handle_legacy().
1825 // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
1826 // handle_legacy() is called internally by set_deserialize().
handle_legacy(t_config_option_key &,std::string &) const1827 virtual void handle_legacy(t_config_option_key &/*opt_key*/, std::string &/*value*/) const {}
1828
1829 public:
1830 using ConfigOptionResolver::option;
1831 using ConfigOptionResolver::option_throw;
1832
1833 // Non-virtual methods:
option(const t_config_option_key & opt_key,bool create=false)1834 ConfigOption* option(const t_config_option_key &opt_key, bool create = false)
1835 { return this->optptr(opt_key, create); }
1836
1837 template<typename TYPE>
option(const t_config_option_key & opt_key,bool create=false)1838 TYPE* option(const t_config_option_key &opt_key, bool create = false)
1839 {
1840 ConfigOption *opt = this->optptr(opt_key, create);
1841 return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
1842 }
1843
option_throw(const t_config_option_key & opt_key,bool create=false)1844 ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false)
1845 {
1846 ConfigOption *opt = this->optptr(opt_key, create);
1847 if (opt == nullptr)
1848 throw UnknownOptionException(opt_key);
1849 return opt;
1850 }
1851
1852 template<typename TYPE>
option_throw(const t_config_option_key & opt_key,bool create=false)1853 TYPE* option_throw(const t_config_option_key &opt_key, bool create = false)
1854 {
1855 ConfigOption *opt = this->option_throw(opt_key, create);
1856 if (opt->type() != TYPE::static_type())
1857 throw BadOptionTypeException("Conversion to a wrong type");
1858 return static_cast<TYPE*>(opt);
1859 }
1860
1861 // Apply all keys of other ConfigBase defined by this->def() to this ConfigBase.
1862 // An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(),
1863 // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
apply(const ConfigBase & other,bool ignore_nonexistent=false)1864 void apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->apply_only(other, other.keys(), ignore_nonexistent); }
1865 // Apply explicitely enumerated keys of other ConfigBase defined by this->def() to this ConfigBase.
1866 // An UnknownOptionException is thrown in case some option keys are not defined by this->def(),
1867 // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
1868 void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false);
equals(const ConfigBase & other) const1869 bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
1870 t_config_option_keys diff(const ConfigBase &other) const;
1871 t_config_option_keys equal(const ConfigBase &other) const;
1872 std::string opt_serialize(const t_config_option_key &opt_key) const;
1873
1874 // Set a value. Convert numeric types using a C style implicit conversion / promotion model.
1875 // Throw if option is not avaiable and create is not enabled,
1876 // or if the conversion is not possible.
1877 // Conversion to string is always possible.
set(const std::string & opt_key,bool value,bool create=false)1878 void set(const std::string &opt_key, bool value, bool create = false)
1879 { this->option_throw<ConfigOptionBool>(opt_key, create)->value = value; }
1880 void set(const std::string &opt_key, int value, bool create = false);
1881 void set(const std::string &opt_key, double value, bool create = false);
set(const std::string & opt_key,const char * value,bool create=false)1882 void set(const std::string &opt_key, const char *value, bool create = false)
1883 { this->option_throw<ConfigOptionString>(opt_key, create)->value = value; }
set(const std::string & opt_key,const std::string & value,bool create=false)1884 void set(const std::string &opt_key, const std::string &value, bool create = false)
1885 { this->option_throw<ConfigOptionString>(opt_key, create)->value = value; }
1886
1887 // Set a configuration value from a string, it will call an overridable handle_legacy()
1888 // to resolve renamed and removed configuration keys.
1889 bool set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions, bool append = false);
1890 // May throw BadOptionTypeException() if the operation fails.
1891 void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext& config_substitutions, bool append = false);
set_deserialize_strict(const t_config_option_key & opt_key,const std::string & str,bool append=false)1892 void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false)
1893 { ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; this->set_deserialize(opt_key, str, ctxt, append); }
1894 struct SetDeserializeItem {
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1895 SetDeserializeItem(const char *opt_key, const char *opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1896 SetDeserializeItem(const std::string &opt_key, const std::string &opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1897 SetDeserializeItem(const char *opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1898 SetDeserializeItem(const std::string &opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1899 SetDeserializeItem(const char *opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1900 SetDeserializeItem(const std::string &opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1901 SetDeserializeItem(const char *opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1902 SetDeserializeItem(const std::string &opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1903 SetDeserializeItem(const char *opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
SetDeserializeItemSlic3r::ConfigBase::SetDeserializeItem1904 SetDeserializeItem(const std::string &opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
1905 std::string opt_key; std::string opt_value; bool append = false;
1906 };
1907 // May throw BadOptionTypeException() if the operation fails.
1908 void set_deserialize(std::initializer_list<SetDeserializeItem> items, ConfigSubstitutionContext& substitutions);
set_deserialize_strict(std::initializer_list<SetDeserializeItem> items)1909 void set_deserialize_strict(std::initializer_list<SetDeserializeItem> items)
1910 { ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; this->set_deserialize(items, ctxt); }
1911
1912 double get_abs_value(const t_config_option_key &opt_key) const;
1913 double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
1914 void setenv_() const;
1915 ConfigSubstitutions load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
1916 ConfigSubstitutions load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
1917 ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
1918 // Returns number of key/value pairs extracted.
1919 size_t load_from_gcode_string(const char* str, ConfigSubstitutionContext& substitutions);
1920 ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule);
1921 void save(const std::string &file) const;
1922
1923 // Set all the nullable values to nils.
1924 void null_nullables();
1925
1926 private:
1927 // Set a configuration value from a string.
1928 bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
1929 };
1930
1931 // Configuration store with dynamic number of configuration values.
1932 // In Slic3r, the dynamic config is mostly used at the user interface layer.
1933 class DynamicConfig : public virtual ConfigBase
1934 {
1935 public:
DynamicConfig()1936 DynamicConfig() {}
DynamicConfig(const DynamicConfig & rhs)1937 DynamicConfig(const DynamicConfig &rhs) { *this = rhs; }
DynamicConfig(DynamicConfig && rhs)1938 DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); }
1939 explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys);
DynamicConfig(const ConfigBase & rhs)1940 explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {}
~DynamicConfig()1941 virtual ~DynamicConfig() override { clear(); }
1942
1943 // Copy a content of one DynamicConfig to another DynamicConfig.
1944 // If rhs.def() is not null, then it has to be equal to this->def().
operator =(const DynamicConfig & rhs)1945 DynamicConfig& operator=(const DynamicConfig &rhs)
1946 {
1947 assert(this->def() == nullptr || this->def() == rhs.def());
1948 this->clear();
1949 for (const auto &kvp : rhs.options)
1950 this->options[kvp.first].reset(kvp.second->clone());
1951 return *this;
1952 }
1953
1954 // Move a content of one DynamicConfig to another DynamicConfig.
1955 // If rhs.def() is not null, then it has to be equal to this->def().
operator =(DynamicConfig && rhs)1956 DynamicConfig& operator=(DynamicConfig &&rhs) noexcept
1957 {
1958 assert(this->def() == nullptr || this->def() == rhs.def());
1959 this->clear();
1960 this->options = std::move(rhs.options);
1961 rhs.options.clear();
1962 return *this;
1963 }
1964
1965 // Add a content of one DynamicConfig to another DynamicConfig.
1966 // If rhs.def() is not null, then it has to be equal to this->def().
operator +=(const DynamicConfig & rhs)1967 DynamicConfig& operator+=(const DynamicConfig &rhs)
1968 {
1969 assert(this->def() == nullptr || this->def() == rhs.def());
1970 for (const auto &kvp : rhs.options) {
1971 auto it = this->options.find(kvp.first);
1972 if (it == this->options.end())
1973 this->options[kvp.first].reset(kvp.second->clone());
1974 else {
1975 assert(it->second->type() == kvp.second->type());
1976 if (it->second->type() == kvp.second->type())
1977 *it->second = *kvp.second;
1978 else
1979 it->second.reset(kvp.second->clone());
1980 }
1981 }
1982 return *this;
1983 }
1984
1985 // Move a content of one DynamicConfig to another DynamicConfig.
1986 // If rhs.def() is not null, then it has to be equal to this->def().
operator +=(DynamicConfig && rhs)1987 DynamicConfig& operator+=(DynamicConfig &&rhs)
1988 {
1989 assert(this->def() == nullptr || this->def() == rhs.def());
1990 for (auto &kvp : rhs.options) {
1991 auto it = this->options.find(kvp.first);
1992 if (it == this->options.end()) {
1993 this->options.insert(std::make_pair(kvp.first, std::move(kvp.second)));
1994 } else {
1995 assert(it->second->type() == kvp.second->type());
1996 it->second = std::move(kvp.second);
1997 }
1998 }
1999 rhs.options.clear();
2000 return *this;
2001 }
2002
2003 bool operator==(const DynamicConfig &rhs) const;
operator !=(const DynamicConfig & rhs) const2004 bool operator!=(const DynamicConfig &rhs) const { return ! (*this == rhs); }
2005
swap(DynamicConfig & other)2006 void swap(DynamicConfig &other)
2007 {
2008 std::swap(this->options, other.options);
2009 }
2010
clear()2011 void clear()
2012 {
2013 this->options.clear();
2014 }
2015
erase(const t_config_option_key & opt_key)2016 bool erase(const t_config_option_key &opt_key)
2017 {
2018 auto it = this->options.find(opt_key);
2019 if (it == this->options.end())
2020 return false;
2021 this->options.erase(it);
2022 return true;
2023 }
2024
2025 // Remove options with all nil values, those are optional and it does not help to hold them.
2026 size_t remove_nil_options();
2027
2028 // Allow DynamicConfig to be instantiated on ints own without a definition.
2029 // If the definition is not defined, the method requiring the definition will throw NoDefinitionException.
def() const2030 const ConfigDef* def() const override { return nullptr; }
opt(const t_config_option_key & opt_key,bool create=false)2031 template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
2032 { return dynamic_cast<T*>(this->option(opt_key, create)); }
opt(const t_config_option_key & opt_key) const2033 template<class T> const T* opt(const t_config_option_key &opt_key) const
2034 { return dynamic_cast<const T*>(this->option(opt_key)); }
2035 // Overrides ConfigResolver::optptr().
2036 const ConfigOption* optptr(const t_config_option_key &opt_key) const override;
2037 // Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name.
2038 ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override;
2039 // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
2040 t_config_option_keys keys() const override;
empty() const2041 bool empty() const { return options.empty(); }
2042
2043 // Set a value for an opt_key. Returns true if the value did not exist yet.
2044 // This DynamicConfig will take ownership of opt.
2045 // Be careful, as this method does not test the existence of opt_key in this->def().
set_key_value(const std::string & opt_key,ConfigOption * opt)2046 bool set_key_value(const std::string &opt_key, ConfigOption *opt)
2047 {
2048 auto it = this->options.find(opt_key);
2049 if (it == this->options.end()) {
2050 this->options[opt_key].reset(opt);
2051 return true;
2052 } else {
2053 it->second.reset(opt);
2054 return false;
2055 }
2056 }
2057
opt_string(const t_config_option_key & opt_key,bool create=false)2058 std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option<ConfigOptionString>(opt_key, create)->value; }
opt_string(const t_config_option_key & opt_key) const2059 const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key); }
opt_string(const t_config_option_key & opt_key,unsigned int idx)2060 std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionStrings>(opt_key)->get_at(idx); }
opt_string(const t_config_option_key & opt_key,unsigned int idx) const2061 const std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key, idx); }
2062
opt_float(const t_config_option_key & opt_key)2063 double& opt_float(const t_config_option_key &opt_key) { return this->option<ConfigOptionFloat>(opt_key)->value; }
opt_float(const t_config_option_key & opt_key) const2064 const double& opt_float(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionFloat*>(this->option(opt_key))->value; }
opt_float(const t_config_option_key & opt_key,unsigned int idx)2065 double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionFloats>(opt_key)->get_at(idx); }
opt_float(const t_config_option_key & opt_key,unsigned int idx) const2066 const double& opt_float(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloats*>(this->option(opt_key))->get_at(idx); }
2067
opt_int(const t_config_option_key & opt_key)2068 int& opt_int(const t_config_option_key &opt_key) { return this->option<ConfigOptionInt>(opt_key)->value; }
opt_int(const t_config_option_key & opt_key) const2069 int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionInt*>(this->option(opt_key))->value; }
opt_int(const t_config_option_key & opt_key,unsigned int idx)2070 int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionInts>(opt_key)->get_at(idx); }
opt_int(const t_config_option_key & opt_key,unsigned int idx) const2071 int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionInts*>(this->option(opt_key))->get_at(idx); }
2072
2073 template<typename ENUM>
opt_enum(const t_config_option_key & opt_key) const2074 ENUM opt_enum(const t_config_option_key &opt_key) const { return (ENUM)dynamic_cast<const ConfigOptionEnumGeneric*>(this->option(opt_key))->value; }
2075
opt_bool(const t_config_option_key & opt_key) const2076 bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
opt_bool(const t_config_option_key & opt_key,unsigned int idx) const2077 bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }
2078
2079 // Command line processing
2080 void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
2081 bool read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
2082
cbegin() const2083 std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cbegin() const { return options.cbegin(); }
cend() const2084 std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); }
size() const2085 size_t size() const { return options.size(); }
2086
2087 private:
2088 std::map<t_config_option_key, std::unique_ptr<ConfigOption>> options;
2089
2090 friend class cereal::access;
serialize(Archive & ar)2091 template<class Archive> void serialize(Archive &ar) { ar(options); }
2092 };
2093
2094 // Configuration store with a static definition of configuration values.
2095 // In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
2096 // because the configuration values could be accessed directly.
2097 class StaticConfig : public virtual ConfigBase
2098 {
2099 public:
StaticConfig()2100 StaticConfig() {}
2101 /// Gets list of config option names for each config option of this->def, which has a static counter-part defined by the derived object
2102 /// and which could be resolved by this->optptr(key) call.
2103 t_config_option_keys keys() const;
2104
2105 protected:
2106 /// Set all statically defined config options to their defaults defined by this->def().
2107 void set_defaults();
2108 };
2109
2110 }
2111
2112 #endif
2113