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