Refactoring: make Slic3r::Config a thin wrapper around Slic3r::DynamicPrintConfig by moving its accessors to Slic3r::ConfigBase

This commit is contained in:
Alessandro Ranellucci 2018-11-28 14:27:33 +01:00 committed by Joseph Lenox
parent 21eb603cc1
commit 79d0677db2
5 changed files with 269 additions and 482 deletions

View File

@ -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<ConfigOptionBool>("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<ConfigOptionBool>("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<ConfigOptionBool>("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<ConfigOptionInt>("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<ConfigOptionInt>("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<ConfigOptionFloat>("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<ConfigOptionString>("deadbeef_invalid_option", false), InvalidOptionType);
REQUIRE_THROWS_AS(config->get<ConfigOptionFloat>("deadbeef_invalid_option", false), InvalidOptionType);
REQUIRE_THROWS_AS(config->get<ConfigOptionInt>("deadbeef_invalid_option", false), InvalidOptionType);
REQUIRE_THROWS_AS(config->get<ConfigOptionBool>("deadbeef_invalid_option", false), InvalidOptionType);
THEN("A BadOptionTypeException exception is thrown.") {
REQUIRE_THROWS_AS(config->get<ConfigOptionString>("deadbeef_invalid_option", false), UnknownOptionException);
REQUIRE_THROWS_AS(config->get<ConfigOptionFloat>("deadbeef_invalid_option", false), UnknownOptionException);
REQUIRE_THROWS_AS(config->get<ConfigOptionInt>("deadbeef_invalid_option", false), UnknownOptionException);
REQUIRE_THROWS_AS(config->get<ConfigOptionBool>("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<ConfigOptionString>("deadbeef_invalid_option", false), InvalidOptionType);
REQUIRE_THROWS_AS(config->get_ptr<ConfigOptionFloat>("deadbeef_invalid_option", false), InvalidOptionType);
REQUIRE_THROWS_AS(config->get_ptr<ConfigOptionInt>("deadbeef_invalid_option", false), InvalidOptionType);
REQUIRE_THROWS_AS(config->get_ptr<ConfigOptionBool>("deadbeef_invalid_option", false), InvalidOptionType);
THEN("A BadOptionTypeException exception is thrown.") {
REQUIRE_THROWS_AS(config->get_ptr<ConfigOptionString>("deadbeef_invalid_option", false), UnknownOptionException);
REQUIRE_THROWS_AS(config->get_ptr<ConfigOptionFloat>("deadbeef_invalid_option", false), UnknownOptionException);
REQUIRE_THROWS_AS(config->get_ptr<ConfigOptionInt>("deadbeef_invalid_option", false), UnknownOptionException);
REQUIRE_THROWS_AS(config->get_ptr<ConfigOptionBool>("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");
}
}
}

View File

@ -1,16 +1,13 @@
#include "Config.hpp"
#include "Log.hpp"
#include <string>
namespace Slic3r {
extern const PrintConfigDef print_config_def;
std::shared_ptr<Config>
Config::new_from_defaults()
{
return std::make_shared<Config>();
std::shared_ptr<Config> my_config(std::make_shared<Config>());
my_config->_config.apply(FullPrintConfig());
return my_config;
}
std::shared_ptr<Config>
Config::new_from_defaults(std::initializer_list<std::string> init)
@ -21,331 +18,17 @@ Config::new_from_defaults(std::initializer_list<std::string> init)
std::shared_ptr<Config>
Config::new_from_defaults(t_config_option_keys init)
{
std::shared_ptr<Config> my_config(std::make_shared<Config>());
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<Config> my_config(std::make_shared<Config>());
my_config->_config.set_defaults(init);
return my_config;
}
std::shared_ptr<Config>
Config::new_from_ini(const std::string& inifile)
{
std::shared_ptr<Config> my_config(std::make_shared<Config>());
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<std::string> 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<ConfigOptionVectorBase>(k);
} catch (std::bad_cast& e) {
throw InvalidOptionType((std::string("(cast failure) Invalid value for ") + std::string(k)).c_str());
}
const std::vector<std::string> 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<ConfigOption>(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<ConfigOptionInt*>(this->_config.optptr(opt_key, true))};
ptr->value = std::stoi(value);
} break;
case coInts:
{
ConfigOptionInts* ptr {dynamic_cast<ConfigOptionInts*>(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<ConfigOptionFloat*>(this->_config.optptr(opt_key, true))};
ptr->setFloat(std::stod(value));
} break;
case coFloatOrPercent:
{
ConfigOptionFloatOrPercent* ptr {dynamic_cast<ConfigOptionFloatOrPercent*>(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<ConfigOptionFloats*>(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<ConfigOptionString*>(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<ConfigOptionBool*>(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<ConfigOptionBool*>(this->_config.optptr(opt_key, true))};
ptr->value = value;
} break;
case coInt:
{
ConfigOptionInt* ptr {dynamic_cast<ConfigOptionInt*>(this->_config.optptr(opt_key, true))};
ptr->setInt(value);
} break;
case coInts:
{
ConfigOptionInts* ptr {dynamic_cast<ConfigOptionInts*>(this->_config.optptr(opt_key, true))};
ptr->deserialize(std::to_string(value), true);
} break;
case coFloat:
{
ConfigOptionFloat* ptr {dynamic_cast<ConfigOptionFloat*>(this->_config.optptr(opt_key, true))};
ptr->setFloat(value);
} break;
case coFloatOrPercent:
{
ConfigOptionFloatOrPercent* ptr {dynamic_cast<ConfigOptionFloatOrPercent*>(this->_config.optptr(opt_key, true))};
ptr->setFloat(value);
ptr->percent = false;
} break;
case coFloats:
{
ConfigOptionFloats* ptr {dynamic_cast<ConfigOptionFloats*>(this->_config.optptr(opt_key, true))};
ptr->deserialize(std::to_string(value), true);
} break;
case coString:
{
ConfigOptionString* ptr {dynamic_cast<ConfigOptionString*>(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<ConfigOptionBool*>(this->_config.optptr(opt_key, true))};
ptr->value = (value != 0);
} break;
case coInt:
{
ConfigOptionInt* ptr {dynamic_cast<ConfigOptionInt*>(this->_config.optptr(opt_key, true))};
ptr->setInt(value);
} break;
case coInts:
{
ConfigOptionInts* ptr {dynamic_cast<ConfigOptionInts*>(this->_config.optptr(opt_key, true))};
ptr->deserialize(std::to_string(value), true);
} break;
case coFloat:
{
ConfigOptionFloat* ptr {dynamic_cast<ConfigOptionFloat*>(this->_config.optptr(opt_key, true))};
ptr->setFloat(value);
} break;
case coFloatOrPercent:
{
ConfigOptionFloatOrPercent* ptr {dynamic_cast<ConfigOptionFloatOrPercent*>(this->_config.optptr(opt_key, true))};
ptr->setFloat(value);
ptr->percent = false;
} break;
case coFloats:
{
ConfigOptionFloats* ptr {dynamic_cast<ConfigOptionFloats*>(this->_config.optptr(opt_key, true))};
ptr->deserialize(std::to_string(value), true);
} break;
case coString:
{
ConfigOptionString* ptr {dynamic_cast<ConfigOptionString*>(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<ConfigOptionInt*>(this->_config.optptr(opt_key, true))};
ptr->setInt(std::round(value));
} break;
case coInts:
{
ConfigOptionInts* ptr {dynamic_cast<ConfigOptionInts*>(this->_config.optptr(opt_key, true))};
ptr->deserialize(std::to_string(std::round(value)), true);
} break;
case coFloat:
{
ConfigOptionFloat* ptr {dynamic_cast<ConfigOptionFloat*>(this->_config.optptr(opt_key, true))};
ptr->setFloat(value);
} break;
case coFloatOrPercent:
{
ConfigOptionFloatOrPercent* ptr {dynamic_cast<ConfigOptionFloatOrPercent*>(this->_config.optptr(opt_key, true))};
ptr->setFloat(value);
ptr->percent = false;
} break;
case coFloats:
{
ConfigOptionFloats* ptr {dynamic_cast<ConfigOptionFloats*>(this->_config.optptr(opt_key, true))};
ptr->deserialize(std::to_string(value), true);
} break;
case coString:
{
ConfigOptionString* ptr {dynamic_cast<ConfigOptionString*>(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

View File

@ -5,30 +5,13 @@
#include <initializer_list>
#include <memory>
#include <regex>
#include <string>
#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<Config>;
using config_ref = std::weak_ptr<Config>;
@ -47,70 +30,67 @@ public:
/// Factory method to construct a Config from an ini file.
static std::shared_ptr<Config> 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<ConfigOption*>(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<ConfigOption*>(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<ConfigOption*>(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<ConfigOption*>(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 <class T>
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<T*>(this->_config.optptr(opt_key, create));
return this->_config.opt_throw<T>(opt_key, create);
}
/// Template function to retrieve and cast in hopefully a slightly nicer
/// format than longwinded dynamic_cast<>
template <class T>
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<T*>(this->_config.optptr(opt_key, create)));
return *this->_config.opt_throw<T>(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

View File

@ -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<ConfigOptionBool>(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<ConfigOptionFloat>(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<ConfigOptionInt>(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<ConfigOptionString>(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<std::string>
ConfigBase::getStrings(const t_config_option_key &opt_key) const {
return this->option_throw(opt_key)->getStrings();
}
std::vector<std::string>
ConfigBase::getStrings(const t_config_option_key &opt_key, std::vector<std::string> default_value) const {
auto opt = this->opt<ConfigOptionStrings>(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<std::string> 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<ConfigBase*>(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<ConfigOptionFloat>(opt_key)->value;
if (value < def.min || value > def.max)
throw InvalidOptionException(opt_key);
} else if (def.type == coFloatOrPercent) {
const auto* opt = this->opt<ConfigOptionFloatOrPercent>(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<ConfigOptionInts>(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

View File

@ -26,6 +26,31 @@ extern std::string escape_strings_cstyle(const std::vector<std::string> &strs);
extern bool unescape_string_cstyle(const std::string &str, std::string &out);
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &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<std::string> getStrings() const { return std::vector<std::string>(); };
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<std::string> getStrings() const { throw BadOptionTypeException(); };
virtual void setStrings(std::vector<std::string> 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>
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>
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<std::string>
public:
ConfigOptionString() : ConfigOptionSingle<std::string>("") {};
ConfigOptionString(std::string _value) : ConfigOptionSingle<std::string>(_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<class T> T* opt(const t_config_option_key &opt_key, bool create = false) {
return dynamic_cast<T*>(this->option(opt_key, create));
};
template<class T> T* opt_throw(const t_config_option_key &opt_key, bool create = false) {
return dynamic_cast<T*>(this->option_throw(opt_key, create));
};
template<class T> const T* opt(const t_config_option_key &opt_key) const {
return dynamic_cast<const T*>(this->option(opt_key));
};
template<class T> const T* opt_throw(const t_config_option_key &opt_key) const {
return dynamic_cast<const T*>(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<std::string> getStrings(const t_config_option_key &opt_key, std::vector<std::string> default_value = std::vector<std::string>()) 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<std::string> getStrings(const t_config_option_key &opt_key) const;
std::vector<std::string> getStrings(const t_config_option_key &opt_key, std::vector<std::string> default_value) const;
void setStrings(const t_config_option_key &opt_key, std::vector<std::string> 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