diff --git a/src/test/libslic3r/test_config.cpp b/src/test/libslic3r/test_config.cpp index 6ed157d47..d024497d9 100644 --- a/src/test/libslic3r/test_config.cpp +++ b/src/test/libslic3r/test_config.cpp @@ -14,16 +14,22 @@ SCENARIO("Generic config validation performs as expected.") { WHEN( "perimeter_extrusion_width is set to 250%, a valid value") { config->set("perimeter_extrusion_width", "250%"); THEN( "The config is read as valid.") { - REQUIRE(config->validate() == true); + auto except_thrown {false}; + try { + config->validate(); + } catch (const InvalidOptionException& e) { + except_thrown = true; + } + REQUIRE(except_thrown == false); } } WHEN( "perimeter_extrusion_width is set to -10, an invalid value") { config->set("perimeter_extrusion_width", -10); - THEN( "An InvalidOptionValue exception is thrown.") { + THEN( "An InvalidOptionException exception is thrown.") { auto except_thrown {false}; try { config->validate(); - } catch (const InvalidOptionValue& e) { + } catch (const InvalidOptionException& e) { except_thrown = true; } REQUIRE(except_thrown == true); @@ -32,11 +38,11 @@ SCENARIO("Generic config validation performs as expected.") { WHEN( "perimeters is set to -10, an invalid value") { config->set("perimeters", -10); - THEN( "An InvalidOptionValue exception is thrown.") { + THEN( "An InvalidOptionException exception is thrown.") { auto except_thrown {false}; try { config->validate(); - } catch (const InvalidOptionValue& e) { + } catch (const InvalidOptionException& e) { except_thrown = true; } REQUIRE(except_thrown == true); @@ -48,25 +54,23 @@ SCENARIO("Generic config validation performs as expected.") { SCENARIO("Config accessor functions perform as expected.") { GIVEN("A config generated from default options") { auto config {Slic3r::Config::new_from_defaults()}; - WHEN("A boolean option is set through the bool interface") { - config->set("gcode_comments", true); - THEN("The underlying value is set correctly.") { - REQUIRE(config->get("gcode_comments").getBool() == true); + WHEN("A boolean option is set to a boolean value") { + THEN("A BadOptionTypeException exception is thrown.") { + REQUIRE_THROWS_AS(config->set("gcode_comments", true), BadOptionTypeException); } } - WHEN("A boolean option is set through the string interface") { + WHEN("A boolean option is set to a string value") { config->set("gcode_comments", "1"); THEN("The underlying value is set correctly.") { REQUIRE(config->get("gcode_comments").getBool() == true); } } - WHEN("A boolean option is set through the int interface") { - config->set("gcode_comments", 1); - THEN("The underlying value is set correctly.") { - REQUIRE(config->get("gcode_comments").getBool() == true); + WHEN("A string option is set to an int value") { + THEN("A BadOptionTypeException exception is thrown.") { + REQUIRE_THROWS_AS(config->set("gcode_comments", 1), BadOptionTypeException); } } - WHEN("A numeric option is set through the string interface") { + WHEN("A numeric option is set from serialized string") { config->set("bed_temperature", "100"); THEN("The underlying value is set correctly.") { REQUIRE(config->get("bed_temperature").getInt() == 100); @@ -92,14 +96,13 @@ SCENARIO("Config accessor functions perform as expected.") { } } WHEN("An integer-based option is set through the double interface") { - config->set("bed_temperature", 5.5); - THEN("The underlying value is set, rounded to the nearest integer.") { - REQUIRE(config->get("bed_temperature").getInt() == 6); + THEN("A BadOptionTypeException exception is thrown.") { + REQUIRE_THROWS_AS(config->set("bed_temperature", 5.5), BadOptionTypeException); } } WHEN("A numeric option is set to a non-numeric value.") { - THEN("An InvalidOptionValue exception is thown.") { - REQUIRE_THROWS_AS(config->set("perimeter_speed", "zzzz"), InvalidOptionValue); + THEN("A BadOptionTypeException exception is thown.") { + REQUIRE_THROWS_AS(config->set("perimeter_speed", "zzzz"), BadOptionTypeException); } THEN("The value does not change.") { REQUIRE(config->get("perimeter_speed").getFloat() == 60.0); @@ -156,36 +159,34 @@ SCENARIO("Config accessor functions perform as expected.") { } } WHEN("An invalid option is requested during set.") { - THEN("An InvalidOptionType exception is thrown.") { - REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", 1), InvalidOptionType); - REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", 1.0), InvalidOptionType); - REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", "1"), InvalidOptionType); - REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", true), InvalidOptionType); + THEN("A BadOptionTypeException exception is thrown.") { + REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", 1), UnknownOptionException); + REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", 1.0), UnknownOptionException); + REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", "1"), UnknownOptionException); + REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", true), UnknownOptionException); } } WHEN("An invalid option is requested during get.") { - THEN("An InvalidOptionType exception is thrown.") { - REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), InvalidOptionType); - REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), InvalidOptionType); - REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), InvalidOptionType); - REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), InvalidOptionType); + THEN("A BadOptionTypeException exception is thrown.") { + REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); + REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); + REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); + REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); } } WHEN("An invalid option is requested during get_ptr.") { - THEN("An InvalidOptionType exception is thrown.") { - REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), InvalidOptionType); - REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), InvalidOptionType); - REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), InvalidOptionType); - REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), InvalidOptionType); + THEN("A BadOptionTypeException exception is thrown.") { + REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); + REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); + REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); + REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); } } WHEN("getX called on an unset option.") { THEN("The default is returned.") { REQUIRE(config->getFloat("layer_height") == 0.3); - REQUIRE(config->getString("layer_height") == "0.3"); - REQUIRE(config->getString("layer_height") == "0.3"); REQUIRE(config->getInt("raft_layers") == 0); REQUIRE(config->getBool("support_material") == false); } @@ -195,7 +196,6 @@ SCENARIO("Config accessor functions perform as expected.") { config->set("layer_height", 0.5); THEN("The set value is returned.") { REQUIRE(config->getFloat("layer_height") == 0.5); - REQUIRE(config->getString("layer_height") == "0.5"); } } } diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 06a760f66..df92bf4ba 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -1,16 +1,13 @@ #include "Config.hpp" -#include "Log.hpp" - -#include namespace Slic3r { -extern const PrintConfigDef print_config_def; - std::shared_ptr Config::new_from_defaults() { - return std::make_shared(); + std::shared_ptr my_config(std::make_shared()); + my_config->_config.apply(FullPrintConfig()); + return my_config; } std::shared_ptr Config::new_from_defaults(std::initializer_list init) @@ -21,331 +18,17 @@ Config::new_from_defaults(std::initializer_list init) std::shared_ptr Config::new_from_defaults(t_config_option_keys init) { - std::shared_ptr my_config(std::make_shared()); - for (auto& opt_key : init) { - if (print_config_def.has(opt_key)) { - const std::string value { print_config_def.get(opt_key).default_value->serialize() }; - my_config->_config.set_deserialize(opt_key, value); - } - } - + std::shared_ptr my_config(std::make_shared()); + my_config->_config.set_defaults(init); return my_config; } std::shared_ptr Config::new_from_ini(const std::string& inifile) { - std::shared_ptr my_config(std::make_shared()); - my_config->read_ini(inifile); + my_config->_config.load(inifile); return my_config; } -// TODO: this should be merged into ConfigBase::validate() -bool -Config::validate() -{ - // general validation - for (auto k : this->_config.keys()) { - if (print_config_def.options.count(k) == 0) continue; // skip over keys that aren't in the master list - const ConfigOptionDef& opt { print_config_def.options.at(k) }; - if (opt.cli == "" || std::regex_search(opt.cli, _match_info, _cli_pattern) == false) continue; - std::string type { _match_info.str(1) }; - std::vector values; - if (std::regex_search(type, _match_info, std::regex("@$"))) { - type = std::regex_replace(type, std::regex("@$"), std::string("")); // strip off the @ for later; - ConfigOptionVectorBase* tmp_opt; - try { - tmp_opt = get_ptr(k); - } catch (std::bad_cast& e) { - throw InvalidOptionType((std::string("(cast failure) Invalid value for ") + std::string(k)).c_str()); - } - const std::vector tmp_str { tmp_opt->vserialize() }; - values.insert(values.end(), tmp_str.begin(), tmp_str.end()); - } else { - Slic3r::Log::debug("Config::validate", std::string("Not an array")); - Slic3r::Log::debug("Config::validate", type); - values.emplace_back(get_ptr(k)->serialize()); - } - // Verify each value - for (auto v : values) { - if (type == "i" || type == "f" || opt.type == coPercent || opt.type == coFloatOrPercent) { - if (opt.type == coPercent || opt.type == coFloatOrPercent) - v = std::regex_replace(v, std::regex("%$"), std::string("")); - if ((type == "i" && !is_valid_int(type, opt, v)) || !is_valid_float(type, opt, v)) { - throw InvalidOptionValue((std::string("Invalid value for ") + std::string(k)).c_str()); - } - } - } - } - return true; -} - -void -Config::set(const t_config_option_key& opt_key, const std::string& value) -{ - try { - const auto& def = print_config_def.options.at(opt_key); - switch (def.type) { - case coInt: - { - ConfigOptionInt* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->value = std::stoi(value); - } break; - case coInts: - { - ConfigOptionInts* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - if (!ptr->deserialize(value, true) ) { - throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); - } - } break; - case coFloat: - { - ConfigOptionFloat* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setFloat(std::stod(value)); - } break; - case coFloatOrPercent: - { - ConfigOptionFloatOrPercent* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - const size_t perc = value.find("%"); - ptr->percent = (perc != std::string::npos); - if (ptr->percent) { - ptr->setFloat(std::stod(std::string(value).replace(value.find("%"), std::string("%").length(), ""))); - ptr->percent = true; - } else { - ptr->setFloat(std::stod(value)); - ptr->percent = false; - } - } break; - case coFloats: - { - ConfigOptionFloats* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - if (!ptr->deserialize(value, true) ) { - throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); - } - } break; - case coString: - { - ConfigOptionString* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - if (!ptr->deserialize(value) ) { - throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); - } - } break; - case coBool: - { - ConfigOptionBool* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->deserialize(value); - } break; - default: - Slic3r::Log::warn("Config::set", "Unknown set type."); - } - } catch (std::invalid_argument& e) { - throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); - } catch (std::out_of_range& e) { - throw InvalidOptionType(std::string(opt_key) + std::string(" is an invalid Slic3r option.")); - } -} - -void -Config::set(const t_config_option_key& opt_key, const bool value) -{ - try { - const auto& def = print_config_def.options.at(opt_key); - switch (def.type) { - case coBool: - { - ConfigOptionBool* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->value = value; - } break; - case coInt: - { - ConfigOptionInt* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setInt(value); - } break; - case coInts: - { - ConfigOptionInts* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->deserialize(std::to_string(value), true); - } break; - case coFloat: - { - ConfigOptionFloat* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setFloat(value); - } break; - case coFloatOrPercent: - { - ConfigOptionFloatOrPercent* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setFloat(value); - ptr->percent = false; - } break; - case coFloats: - { - ConfigOptionFloats* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->deserialize(std::to_string(value), true); - } break; - case coString: - { - ConfigOptionString* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - if (!ptr->deserialize(std::to_string(value)) ) { - throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); - } - } break; - default: - Slic3r::Log::warn("Config::set", "Unknown set type."); - } - - } catch (std::out_of_range &e) { - throw InvalidOptionType(std::string(opt_key) + std::string(" is an invalid Slic3r option.")); - } - -} -void -Config::set(const t_config_option_key& opt_key, const int value) -{ - try { - const auto& def = print_config_def.options.at(opt_key); - switch (def.type) { - case coBool: - { - ConfigOptionBool* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->value = (value != 0); - } break; - case coInt: - { - ConfigOptionInt* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setInt(value); - } break; - case coInts: - { - ConfigOptionInts* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->deserialize(std::to_string(value), true); - } break; - case coFloat: - { - ConfigOptionFloat* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setFloat(value); - } break; - case coFloatOrPercent: - { - ConfigOptionFloatOrPercent* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setFloat(value); - ptr->percent = false; - } break; - case coFloats: - { - ConfigOptionFloats* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->deserialize(std::to_string(value), true); - } break; - case coString: - { - ConfigOptionString* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - if (!ptr->deserialize(std::to_string(value)) ) { - throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); - } - } break; - default: - Slic3r::Log::warn("Config::set", "Unknown set type."); - } - - } catch (std::out_of_range &e) { - throw InvalidOptionType(std::string(opt_key) + std::string(" is an invalid Slic3r option.")); - } -} - -void -Config::set(const t_config_option_key& opt_key, const double value) -{ - try { - const auto& def = print_config_def.options.at(opt_key); - switch (def.type) { - case coInt: - { - ConfigOptionInt* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setInt(std::round(value)); - } break; - case coInts: - { - ConfigOptionInts* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->deserialize(std::to_string(std::round(value)), true); - } break; - case coFloat: - { - ConfigOptionFloat* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setFloat(value); - } break; - case coFloatOrPercent: - { - ConfigOptionFloatOrPercent* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->setFloat(value); - ptr->percent = false; - } break; - case coFloats: - { - ConfigOptionFloats* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - ptr->deserialize(std::to_string(value), true); - } break; - case coString: - { - ConfigOptionString* ptr {dynamic_cast(this->_config.optptr(opt_key, true))}; - if (!ptr->deserialize(std::to_string(value)) ) { - throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); - } - } break; - default: - Slic3r::Log::warn("Config::set", "Unknown set type."); - } - } catch (std::out_of_range &e) { - throw InvalidOptionType(std::string(opt_key) + std::string(" is an invalid Slic3r option.")); - } -} - -void -Config::read_ini(const std::string& file) -{ - this->_config.load(file); -} - -void -Config::write_ini(const std::string& file) const -{ -} - -bool -is_valid_int(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value) -{ - std::regex _valid_int {"^-?\\d+$"}; - std::smatch match; - ConfigOptionInt tmp; - - bool result {type == "i"}; - if (result) - result = result && std::regex_search(ser_value, match, _valid_int); - if (result) { - tmp.deserialize(ser_value); - result = result & (tmp.getInt() <= opt.max && tmp.getInt() >= opt.min); - } - - return result; -} - -bool -is_valid_float(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value) -{ - std::regex _valid_float {"^-?(?:\\d+|\\d*\\.\\d+)$"}; - std::smatch match; - ConfigOptionFloat tmp; - Slic3r::Log::debug("is_valid_float", ser_value); - - bool result {type == "f" || opt.type == coPercent || opt.type == coFloatOrPercent}; - if (result) - result = result && std::regex_search(ser_value, match, _valid_float); - if (result) { - tmp.deserialize(ser_value); - result = result & (tmp.getFloat() <= opt.max && tmp.getFloat() >= opt.min); - } - return result; -} - -Config::Config() : _config(DynamicPrintConfig()) {}; - } // namespace Slic3r diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 5a01c3771..fd4b29837 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -5,30 +5,13 @@ #include #include #include +#include #include "PrintConfig.hpp" #include "ConfigBase.hpp" namespace Slic3r { -/// Exception class for invalid (but correct type) option values. -/// Thrown by validate() -class InvalidOptionValue : public std::runtime_error { -public: - InvalidOptionValue(const char* v) : runtime_error(v) {} - InvalidOptionValue(const std::string v) : runtime_error(v.c_str()) {} -}; - -/// Exception class to handle config options that don't exist. -class InvalidConfigOption : public std::runtime_error {}; - -/// Exception class for type mismatches -class InvalidOptionType : public std::runtime_error { -public: - InvalidOptionType(const char* v) : runtime_error(v) {} - InvalidOptionType(const std::string v) : runtime_error(v.c_str()) {} -}; - class Config; using config_ptr = std::shared_ptr; using config_ref = std::weak_ptr; @@ -47,70 +30,67 @@ public: /// Factory method to construct a Config from an ini file. static std::shared_ptr new_from_ini(const std::string& inifile); - - /// Write a windows-style opt=value ini file with categories from the configuration store. - void write_ini(const std::string& file) const; - - /// Parse a windows-style opt=value ini file with categories and load the configuration store. - void read_ini(const std::string& file); - - - double getFloat(const t_config_option_key& opt_key, bool create=true) { - if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); - return (dynamic_cast(this->_config.optptr(opt_key, create)))->getFloat(); + + double getFloat(const t_config_option_key& opt_key) const { + return this->_config.getFloat(opt_key); } - int getInt(const t_config_option_key& opt_key, bool create=true) { - if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); - return (dynamic_cast(this->_config.optptr(opt_key, create)))->getInt(); + int getInt(const t_config_option_key& opt_key) const { + return this->_config.getInt(opt_key); } - bool getBool(const t_config_option_key& opt_key, bool create=true) { - if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); - return (dynamic_cast(this->_config.optptr(opt_key, create)))->getBool(); + bool getBool(const t_config_option_key& opt_key) const { + return this->_config.getBool(opt_key); } - std::string getString(const t_config_option_key& opt_key, bool create=true) { - if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); - return (dynamic_cast(this->_config.optptr(opt_key, create)))->getString(); + std::string getString(const t_config_option_key& opt_key) const { + return this->_config.getString(opt_key); } /// Template function to dynamic cast and leave it in pointer form. template T* get_ptr(const t_config_option_key& opt_key, bool create=true) { - if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); - return dynamic_cast(this->_config.optptr(opt_key, create)); + return this->_config.opt_throw(opt_key, create); } /// Template function to retrieve and cast in hopefully a slightly nicer /// format than longwinded dynamic_cast<> template T& get(const t_config_option_key& opt_key, bool create=true) { - if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); - return *(dynamic_cast(this->_config.optptr(opt_key, create))); + return *this->_config.opt_throw(opt_key, create); } /// Function to parse value from a string to whatever opt_key is. - void set(const t_config_option_key& opt_key, const std::string& value); + void set(const t_config_option_key& opt_key, const std::string& value) { + this->_config.set_deserialize_throw(opt_key, value); + }; - void set(const t_config_option_key& opt_key, const char* value) { this->set(opt_key, std::string(value));} + void set(const t_config_option_key& opt_key, const char* value) { + this->set(opt_key, std::string(value)); + }; /// Function to parse value from an integer to whatever opt_key is, if /// opt_key is a numeric type. This will throw an exception and do /// nothing if called with an incorrect type. - void set(const t_config_option_key& opt_key, const int value); + void set(const t_config_option_key& opt_key, const int value) { + this->_config.setInt(opt_key, value); + }; /// Function to parse value from an boolean to whatever opt_key is, if /// opt_key is a numeric type. This will throw an exception and do /// nothing if called with an incorrect type. - void set(const t_config_option_key& opt_key, const bool value); + void set(const t_config_option_key& opt_key, const bool value) { + this->_config.setBool(opt_key, value); + }; /// Function to parse value from an integer to whatever opt_key is, if /// opt_key is a numeric type. This will throw an exception and do /// nothing if called with an incorrect type. - void set(const t_config_option_key& opt_key, const double value); + void set(const t_config_option_key& opt_key, const double value) { + this->_config.setFloat(opt_key, value); + }; /// Method to validate the different configuration options. - /// It will throw InvalidConfigOption exceptions on failure. - bool validate(); + /// It will throw InvalidOptionException exceptions on failure. + void validate() { this->_config.validate(); }; const DynamicPrintConfig& config() const { return _config; } bool empty() const { return _config.empty(); } @@ -134,20 +114,13 @@ public: bool has(const t_config_option_key& k) const { return _config.has(k); }; /// Do not use; prefer static factory methods instead. - Config(); + Config() : _config(DynamicPrintConfig()) {}; private: - std::regex _cli_pattern {"=(.+)$"}; - std::smatch _match_info {}; - - /// Underlying configuration store. DynamicPrintConfig _config {}; }; -bool is_valid_int(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value); -bool is_valid_float(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value); - } // namespace Slic3r #endif // CONFIG_HPP diff --git a/xs/src/libslic3r/ConfigBase.cpp b/xs/src/libslic3r/ConfigBase.cpp index 091cd5361..c82f48bed 100644 --- a/xs/src/libslic3r/ConfigBase.cpp +++ b/xs/src/libslic3r/ConfigBase.cpp @@ -397,6 +397,16 @@ ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &opt_ } } +void +ConfigBase::set_defaults(const t_config_option_keys &opt_keys) +{ + // use defaults from definition + if (this->def == NULL) return; + for (auto opt_key : opt_keys) + if (this->def->has(opt_key)) + this->option(opt_key, true)->set(*this->def->options.at(opt_key).default_value); +} + bool ConfigBase::equals(const ConfigBase &other) const { return this->diff(other).empty(); @@ -451,6 +461,12 @@ ConfigBase::set_deserialize(t_config_option_key opt_key, std::string str, bool a return opt->deserialize(str, append); } +void +ConfigBase::set_deserialize_throw(t_config_option_key opt_key, std::string str, bool append) { + bool res = this->set_deserialize(opt_key, str, append); + if (!res) throw BadOptionTypeException(); +} + // Return an absolute value of a possibly relative config variable. // For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height. double @@ -482,34 +498,84 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) return opt->get_abs_value(ratio_over); } +bool +ConfigBase::getBool(const t_config_option_key &opt_key) const { + return this->option_throw(opt_key)->getBool(); +} + bool ConfigBase::getBool(const t_config_option_key &opt_key, bool default_value) const { - auto opt = this->opt(opt_key); - return opt == nullptr ? default_value : opt->value; + const ConfigOption* opt = this->option(opt_key); + return opt == nullptr ? default_value : opt->getBool(); +} + +void +ConfigBase::setBool(const t_config_option_key &opt_key, bool value) { + this->option(opt_key, true)->setBool(value); +} + +double +ConfigBase::getFloat(const t_config_option_key &opt_key) const { + return this->option_throw(opt_key)->getFloat(); } double ConfigBase::getFloat(const t_config_option_key &opt_key, double default_value) const { - auto opt = this->opt(opt_key); - return opt == nullptr ? default_value : opt->value; + const ConfigOption* opt = this->option(opt_key); + return opt == nullptr ? default_value : opt->getFloat(); +} + +void +ConfigBase::setFloat(const t_config_option_key &opt_key, double value) { + this->option(opt_key, true)->setFloat(value); } int -ConfigBase::getInt(const t_config_option_key &opt_key, double default_value) const { - auto opt = this->opt(opt_key); - return opt == nullptr ? default_value : opt->value; +ConfigBase::getInt(const t_config_option_key &opt_key) const { + return this->option_throw(opt_key)->getInt(); +} + +int +ConfigBase::getInt(const t_config_option_key &opt_key, int default_value) const { + const ConfigOption* opt = this->option(opt_key); + return opt == nullptr ? default_value : opt->getInt(); +} + +void +ConfigBase::setInt(const t_config_option_key &opt_key, int value) { + this->option(opt_key, true)->setInt(value); +} + +std::string +ConfigBase::getString(const t_config_option_key &opt_key) const { + return this->option_throw(opt_key)->getString(); } std::string ConfigBase::getString(const t_config_option_key &opt_key, std::string default_value) const { - auto opt = this->opt(opt_key); - return opt == nullptr ? default_value : opt->value; + const ConfigOption* opt = this->option(opt_key); + return opt == nullptr ? default_value : opt->getString(); +} + +void +ConfigBase::setString(const t_config_option_key &opt_key, std::string value) { + this->option(opt_key, true)->setString(value); +} + +std::vector +ConfigBase::getStrings(const t_config_option_key &opt_key) const { + return this->option_throw(opt_key)->getStrings(); } std::vector ConfigBase::getStrings(const t_config_option_key &opt_key, std::vector default_value) const { - auto opt = this->opt(opt_key); - return opt == nullptr ? default_value : opt->values; + const ConfigOption* opt = this->option(opt_key); + return opt == nullptr ? default_value : opt->getStrings(); +} + +void +ConfigBase::setStrings(const t_config_option_key &opt_key, std::vector value) { + this->option(opt_key, true)->setStrings(value); } void @@ -527,11 +593,25 @@ ConfigBase::option(const t_config_option_key &opt_key) const { return const_cast(this)->option(opt_key, false); } +const ConfigOption* +ConfigBase::option_throw(const t_config_option_key &opt_key) const { + const auto opt = this->option(opt_key); + if (opt == nullptr) throw UnknownOptionException(opt_key); + return opt; +} + ConfigOption* ConfigBase::option(const t_config_option_key &opt_key, bool create) { return this->optptr(opt_key, create); } +ConfigOption* +ConfigBase::option_throw(const t_config_option_key &opt_key, bool create) { + auto opt = this->optptr(opt_key, create); + if (opt == nullptr) throw UnknownOptionException(opt_key); + return opt; +} + void ConfigBase::load(const std::string &file) { @@ -587,6 +667,11 @@ ConfigBase::validate() const auto &value = this->opt(opt_key)->value; if (value < def.min || value > def.max) throw InvalidOptionException(opt_key); + } else if (def.type == coFloatOrPercent) { + const auto* opt = this->opt(opt_key); + auto &value = opt->value; + if (!opt->percent && (value < def.min || value > def.max)) + throw InvalidOptionException(opt_key); } else if (def.type == coInts) { for (auto &value : this->opt(opt_key)->values) if (value < def.min || value > def.max) @@ -596,7 +681,6 @@ ConfigBase::validate() const if (value < def.min || value > def.max) throw InvalidOptionException(opt_key); } - // TODO: validate coFloatOrPercent (semantics of min/max are ambiguous for it) } } @@ -629,7 +713,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { if (this->options.count(opt_key) == 0) { if (create) { - if (!this->def->has(opt_key)) return nullptr; + if (!this->def->has(opt_key)) throw UnknownOptionException(opt_key); const ConfigOptionDef& optdef = this->def->options.at(opt_key); ConfigOption* opt; if (optdef.default_value != nullptr) { @@ -811,11 +895,7 @@ DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra, t_co void StaticConfig::set_defaults() { - // use defaults from definition - if (this->def == NULL) return; - for (auto opt_key : this->keys()) - if (this->def->has(opt_key)) - this->option(opt_key)->set(*this->def->options.at(opt_key).default_value); + ConfigBase::set_defaults(this->keys()); } t_config_option_keys diff --git a/xs/src/libslic3r/ConfigBase.hpp b/xs/src/libslic3r/ConfigBase.hpp index 1e77382a5..7805f0fd1 100644 --- a/xs/src/libslic3r/ConfigBase.hpp +++ b/xs/src/libslic3r/ConfigBase.hpp @@ -26,6 +26,31 @@ extern std::string escape_strings_cstyle(const std::vector &strs); extern bool unescape_string_cstyle(const std::string &str, std::string &out); extern bool unescape_strings_cstyle(const std::string &str, std::vector &out); +/// Specialization of std::exception to indicate that an unknown config option has been encountered. +class ConfigOptionException : public std::exception { + public: + t_config_option_key opt_key; + ConfigOptionException(t_config_option_key _opt_key) + : opt_key(_opt_key) {}; +}; +class UnknownOptionException : public ConfigOptionException { + using ConfigOptionException::ConfigOptionException; +}; +class InvalidOptionException : public ConfigOptionException { + using ConfigOptionException::ConfigOptionException; + + public: + virtual const char* what() const noexcept { + std::string s("Invalid value for option: "); + s += this->opt_key; + return s.c_str(); + } +}; + +/// Specialization of std::exception to indicate that an unsupported accessor was called on a config option. +class BadOptionTypeException : public std::exception {}; + + /// \brief Public interface for configuration options. /// /// Defines get/set for all supported data types. @@ -38,14 +63,22 @@ class ConfigOption { virtual std::string serialize() const = 0; virtual bool deserialize(std::string str, bool append = false) = 0; virtual void set(const ConfigOption &option) = 0; - virtual int getInt() const { return 0; }; - virtual double getFloat() const { return 0; }; - virtual bool getBool() const { return false; }; - virtual void setInt(int val) {}; - virtual void setFloat(double val) {}; - virtual void setString(std::string val) {}; - virtual std::string getString() const { return ""; }; - virtual std::vector getStrings() const { return std::vector(); }; + + virtual bool getBool() const { throw BadOptionTypeException(); }; + virtual void setBool(bool val) { throw BadOptionTypeException(); }; + + virtual int getInt() const { throw BadOptionTypeException(); }; + virtual void setInt(int val) { throw BadOptionTypeException(); }; + + virtual double getFloat() const { throw BadOptionTypeException(); }; + virtual void setFloat(double val) { throw BadOptionTypeException(); }; + + virtual std::string getString() const { throw BadOptionTypeException(); }; + virtual void setString(std::string val) { throw BadOptionTypeException(); }; + + virtual std::vector getStrings() const { throw BadOptionTypeException(); }; + virtual void setStrings(std::vector val) { throw BadOptionTypeException(); }; + friend bool operator== (const ConfigOption &a, const ConfigOption &b); friend bool operator!= (const ConfigOption &a, const ConfigOption &b); }; @@ -108,7 +141,6 @@ class ConfigOptionFloat : public ConfigOptionSingle double getFloat() const override { return this->value; }; void setFloat(double val) override { this->value = val; } void setInt(int val) override { this->value = val; } - std::string getString() const override { return trim_zeroes(std::to_string(this->value)); } std::string serialize() const override { std::ostringstream ss; @@ -178,7 +210,6 @@ class ConfigOptionInt : public ConfigOptionSingle int getInt() const override { return this->value; }; void setInt(int val) override { this->value = val; }; - std::string getString() const override { return std::to_string(this->value); } std::string serialize() const override { std::ostringstream ss; @@ -239,15 +270,18 @@ class ConfigOptionString : public ConfigOptionSingle public: ConfigOptionString() : ConfigOptionSingle("") {}; ConfigOptionString(std::string _value) : ConfigOptionSingle(_value) {}; - ConfigOptionString* clone() const { return new ConfigOptionString(this->value); }; + ConfigOptionString* clone() const override { return new ConfigOptionString(this->value); }; - std::string getString() const { return this->value; }; + std::string getString() const override { return this->value; }; + void setString(std::string val) override { this->value = val; }; + void setInt(int val) override { this->value = std::to_string(val); }; + void setFloat(double val) override { this->value = std::to_string(val); }; - std::string serialize() const { + std::string serialize() const override { return escape_string_cstyle(this->value); }; - bool deserialize(std::string str, bool append = false) { + bool deserialize(std::string str, bool append = false) override { return unescape_string_cstyle(str, this->value); }; }; @@ -317,9 +351,19 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {}; ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {}; - ConfigOptionFloatOrPercent* clone() const { return new ConfigOptionFloatOrPercent(this->value, this->percent); }; + ConfigOptionFloatOrPercent* clone() const override { return new ConfigOptionFloatOrPercent(this->value, this->percent); }; - void set(const ConfigOption &option) { + double getFloat() const override { throw BadOptionTypeException(); }; + void setFloat(double val) override { + this->value = val; + this->percent = false; + } + void setInt(int val) override { + this->value = val; + this->percent = false; + } + + void set(const ConfigOption &option) override { const ConfigOptionFloatOrPercent* other = dynamic_cast< const ConfigOptionFloatOrPercent* >(&option); if (other != NULL) { this->value = other->value; @@ -335,7 +379,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent } }; - std::string serialize() const { + std::string serialize() const override { std::ostringstream ss; ss << this->value; std::string s(ss.str()); @@ -343,7 +387,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent return s; }; - bool deserialize(std::string str, bool append = false) { + bool deserialize(std::string str, bool append = false) override { this->percent = str.find_first_of("%") != std::string::npos; std::istringstream iss(str); iss >> this->value; @@ -706,18 +750,29 @@ class ConfigBase virtual ~ConfigBase() {}; bool has(const t_config_option_key &opt_key) const; const ConfigOption* option(const t_config_option_key &opt_key) const; + const ConfigOption* option_throw(const t_config_option_key &opt_key) const; ConfigOption* option(const t_config_option_key &opt_key, bool create = false); + ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false); template T* opt(const t_config_option_key &opt_key, bool create = false) { return dynamic_cast(this->option(opt_key, create)); }; + template T* opt_throw(const t_config_option_key &opt_key, bool create = false) { + return dynamic_cast(this->option_throw(opt_key, create)); + }; template const T* opt(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key)); }; + template const T* opt_throw(const t_config_option_key &opt_key) const { + return dynamic_cast(this->option_throw(opt_key)); + }; virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; virtual t_config_option_keys keys() const = 0; void apply(const ConfigBase &other, bool ignore_nonexistent = false); void apply_only(const ConfigBase &other, const t_config_option_keys &opt_keys, bool ignore_nonexistent = false); - + + /// Set the given config options to their defaults defined by PrintConfigDef. + void set_defaults(const t_config_option_keys &opt_keys); + /// Apply one configuration store to another. /// @param other configuration store to apply from /// @param opt_keys Vector of string keys to apply one to the other @@ -727,14 +782,31 @@ class ConfigBase bool equals(const ConfigBase &other) const; t_config_option_keys diff(const ConfigBase &other) const; std::string serialize(const t_config_option_key &opt_key) const; - virtual bool set_deserialize(t_config_option_key opt_key, std::string str, bool append = false); + bool set_deserialize(t_config_option_key opt_key, std::string str, bool append = false); + void set_deserialize_throw(t_config_option_key opt_key, std::string str, bool append = false); double get_abs_value(const t_config_option_key &opt_key) const; double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; - bool getBool(const t_config_option_key &opt_key, bool default_value = false) const; - double getFloat(const t_config_option_key &opt_key, double default_value = 0.0) const; - int getInt(const t_config_option_key &opt_key, double default_value = 0) const; - std::string getString(const t_config_option_key &opt_key, std::string default_value = "") const; - std::vector getStrings(const t_config_option_key &opt_key, std::vector default_value = std::vector()) const; + + bool getBool(const t_config_option_key &opt_key) const; + bool getBool(const t_config_option_key &opt_key, bool default_value) const; + void setBool(const t_config_option_key &opt_key, bool value); + + double getFloat(const t_config_option_key &opt_key) const; + double getFloat(const t_config_option_key &opt_key, double default_value) const; + void setFloat(const t_config_option_key &opt_key, double value); + + int getInt(const t_config_option_key &opt_key) const; + int getInt(const t_config_option_key &opt_key, int default_value) const; + void setInt(const t_config_option_key &opt_key, int value); + + std::string getString(const t_config_option_key &opt_key) const; + std::string getString(const t_config_option_key &opt_key, std::string default_value) const; + void setString(const t_config_option_key &opt_key, std::string value); + + std::vector getStrings(const t_config_option_key &opt_key) const; + std::vector getStrings(const t_config_option_key &opt_key, std::vector default_value) const; + void setStrings(const t_config_option_key &opt_key, std::vector value); + void setenv_(); void load(const std::string &file); void save(const std::string &file) const; @@ -781,27 +853,6 @@ class StaticConfig : public virtual ConfigBase /// virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; }; -/// Specialization of std::exception to indicate that an unknown config option has been encountered. -class ConfigOptionException : public std::exception { - public: - t_config_option_key opt_key; - ConfigOptionException(t_config_option_key _opt_key) - : opt_key(_opt_key) {}; -}; -class UnknownOptionException : public ConfigOptionException { - using ConfigOptionException::ConfigOptionException; -}; -class InvalidOptionException : public ConfigOptionException { - using ConfigOptionException::ConfigOptionException; - - public: - virtual const char* what() const noexcept { - std::string s("Invalid value for option: "); - s += this->opt_key; - return s.c_str(); - } -}; - } #endif