Rework of Add random function for gcode macros. #5219 after merge:
The thread local storage variables for the random number generator were replaced by an external context variable. Thread local storage is a scarce resource.
This commit is contained in:
parent
1e190affcf
commit
7353d8c6e9
4 changed files with 48 additions and 23 deletions
|
@ -1340,7 +1340,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
||||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return m_placeholder_parser.process(templ, current_extruder_id, config_override);
|
return m_placeholder_parser.process(templ, current_extruder_id, config_override, &m_placeholder_parser_context);
|
||||||
} catch (std::runtime_error &err) {
|
} catch (std::runtime_error &err) {
|
||||||
// Collect the names of failed template substitutions for error reporting.
|
// Collect the names of failed template substitutions for error reporting.
|
||||||
auto it = m_placeholder_parser_failed_templates.find(name);
|
auto it = m_placeholder_parser_failed_templates.find(name);
|
||||||
|
|
|
@ -300,6 +300,8 @@ private:
|
||||||
FullPrintConfig m_config;
|
FullPrintConfig m_config;
|
||||||
GCodeWriter m_writer;
|
GCodeWriter m_writer;
|
||||||
PlaceholderParser m_placeholder_parser;
|
PlaceholderParser m_placeholder_parser;
|
||||||
|
// For random number generator etc.
|
||||||
|
PlaceholderParser::ContextData m_placeholder_parser_context;
|
||||||
// Collection of templates, on which the placeholder substitution failed.
|
// Collection of templates, on which the placeholder substitution failed.
|
||||||
std::map<std::string, std::string> m_placeholder_parser_failed_templates;
|
std::map<std::string, std::string> m_placeholder_parser_failed_templates;
|
||||||
OozePrevention m_ooze_prevention;
|
OozePrevention m_ooze_prevention;
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <random>
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#include <stdlib.h> // provides **_environ
|
#include <stdlib.h> // provides **_environ
|
||||||
#else
|
#else
|
||||||
|
@ -497,35 +496,26 @@ namespace client
|
||||||
static void leq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '>', true ); }
|
static void leq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '>', true ); }
|
||||||
static void geq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '<', true ); }
|
static void geq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '<', true ); }
|
||||||
|
|
||||||
// Random number generators
|
static void throw_if_not_numeric(const expr ¶m)
|
||||||
static int random_int(int min, int max) {
|
{
|
||||||
thread_local static std::mt19937 engine(std::random_device{}());
|
const char *err_msg = "Not a numeric type.";
|
||||||
thread_local static std::uniform_int_distribution<int> dist;
|
param.throw_if_not_numeric(err_msg);
|
||||||
return dist(engine, decltype(dist)::param_type{ min, max });
|
|
||||||
}
|
|
||||||
static double random_double(double min, double max) {
|
|
||||||
thread_local static std::mt19937 engine(std::random_device{}());
|
|
||||||
thread_local static std::uniform_real_distribution<double> dist;
|
|
||||||
return dist(engine, decltype(dist)::param_type{ min, max });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Function2ParamsType {
|
enum Function2ParamsType {
|
||||||
FUNCTION_MIN,
|
FUNCTION_MIN,
|
||||||
FUNCTION_MAX,
|
FUNCTION_MAX,
|
||||||
FUNCTION_RANDOM,
|
|
||||||
};
|
};
|
||||||
// Store the result into param1.
|
// Store the result into param1.
|
||||||
static void function_2params(expr ¶m1, expr ¶m2, Function2ParamsType fun)
|
static void function_2params(expr ¶m1, expr ¶m2, Function2ParamsType fun)
|
||||||
{
|
{
|
||||||
const char *err_msg = "Not a numeric type.";
|
throw_if_not_numeric(param1);
|
||||||
param1.throw_if_not_numeric(err_msg);
|
throw_if_not_numeric(param2);
|
||||||
param2.throw_if_not_numeric(err_msg);
|
|
||||||
if (param1.type == TYPE_DOUBLE || param2.type == TYPE_DOUBLE) {
|
if (param1.type == TYPE_DOUBLE || param2.type == TYPE_DOUBLE) {
|
||||||
double d = 0.;
|
double d = 0.;
|
||||||
switch (fun) {
|
switch (fun) {
|
||||||
case FUNCTION_MIN: d = std::min(param1.as_d(), param2.as_d()); break;
|
case FUNCTION_MIN: d = std::min(param1.as_d(), param2.as_d()); break;
|
||||||
case FUNCTION_MAX: d = std::max(param1.as_d(), param2.as_d()); break;
|
case FUNCTION_MAX: d = std::max(param1.as_d(), param2.as_d()); break;
|
||||||
case FUNCTION_RANDOM: d = random_double(param1.as_d(), param2.as_d()); break;
|
|
||||||
default: param1.throw_exception("Internal error: invalid function");
|
default: param1.throw_exception("Internal error: invalid function");
|
||||||
}
|
}
|
||||||
param1.data.d = d;
|
param1.data.d = d;
|
||||||
|
@ -535,7 +525,6 @@ namespace client
|
||||||
switch (fun) {
|
switch (fun) {
|
||||||
case FUNCTION_MIN: i = std::min(param1.as_i(), param2.as_i()); break;
|
case FUNCTION_MIN: i = std::min(param1.as_i(), param2.as_i()); break;
|
||||||
case FUNCTION_MAX: i = std::max(param1.as_i(), param2.as_i()); break;
|
case FUNCTION_MAX: i = std::max(param1.as_i(), param2.as_i()); break;
|
||||||
case FUNCTION_RANDOM: i = random_int(param1.as_i(), param2.as_i()); break;
|
|
||||||
default: param1.throw_exception("Internal error: invalid function");
|
default: param1.throw_exception("Internal error: invalid function");
|
||||||
}
|
}
|
||||||
param1.data.i = i;
|
param1.data.i = i;
|
||||||
|
@ -545,7 +534,20 @@ namespace client
|
||||||
// Store the result into param1.
|
// Store the result into param1.
|
||||||
static void min(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MIN); }
|
static void min(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MIN); }
|
||||||
static void max(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MAX); }
|
static void max(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MAX); }
|
||||||
static void random(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_RANDOM); }
|
|
||||||
|
// Store the result into param1.
|
||||||
|
static void random(expr ¶m1, expr ¶m2, std::mt19937 &rng)
|
||||||
|
{
|
||||||
|
throw_if_not_numeric(param1);
|
||||||
|
throw_if_not_numeric(param2);
|
||||||
|
if (param1.type == TYPE_DOUBLE || param2.type == TYPE_DOUBLE) {
|
||||||
|
param1.data.d = std::uniform_real_distribution<>(param1.as_d(), param2.as_d())(rng);
|
||||||
|
param1.type = TYPE_DOUBLE;
|
||||||
|
} else {
|
||||||
|
param1.data.i = std::uniform_int_distribution<>(param1.as_i(), param2.as_i())(rng);
|
||||||
|
param1.type = TYPE_INT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op)
|
static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op)
|
||||||
{
|
{
|
||||||
|
@ -641,6 +643,7 @@ namespace client
|
||||||
const DynamicConfig *config = nullptr;
|
const DynamicConfig *config = nullptr;
|
||||||
const DynamicConfig *config_override = nullptr;
|
const DynamicConfig *config_override = nullptr;
|
||||||
size_t current_extruder_id = 0;
|
size_t current_extruder_id = 0;
|
||||||
|
PlaceholderParser::ContextData *context_data = nullptr;
|
||||||
// If false, the macro_processor will evaluate a full macro.
|
// If false, the macro_processor will evaluate a full macro.
|
||||||
// If true, the macro processor will evaluate just a boolean condition using the full expressive power of the macro processor.
|
// If true, the macro processor will evaluate just a boolean condition using the full expressive power of the macro processor.
|
||||||
bool just_boolean_expression = false;
|
bool just_boolean_expression = false;
|
||||||
|
@ -841,6 +844,15 @@ namespace client
|
||||||
output = expr_index.i();
|
output = expr_index.i();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void random(const MyContext *ctx, expr<Iterator> ¶m1, expr<Iterator> ¶m2)
|
||||||
|
{
|
||||||
|
if (ctx->context_data == nullptr)
|
||||||
|
ctx->throw_exception("Random number generator not available in this context.",
|
||||||
|
boost::iterator_range<Iterator>(param1.it_range.begin(), param2.it_range.end()));
|
||||||
|
expr<Iterator>::random(param1, param2, ctx->context_data->rng);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
static void throw_exception(const std::string &msg, const boost::iterator_range<Iterator> &it_range)
|
static void throw_exception(const std::string &msg, const boost::iterator_range<Iterator> &it_range)
|
||||||
{
|
{
|
||||||
|
@ -1194,7 +1206,7 @@ namespace client
|
||||||
| (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
|
| (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
|
||||||
[ px::bind(&expr<Iterator>::max, _val, _2) ]
|
[ px::bind(&expr<Iterator>::max, _val, _2) ]
|
||||||
| (kw["random"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
|
| (kw["random"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
|
||||||
[ px::bind(&expr<Iterator>::random, _val, _2) ]
|
[ px::bind(&MyContext::random<Iterator>, _r1, _val, _2) ]
|
||||||
| (kw["int"] > '(' > unary_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
|
| (kw["int"] > '(' > unary_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
|
||||||
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
|
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
|
||||||
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
||||||
|
@ -1332,13 +1344,14 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
|
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, ContextData *context_data) const
|
||||||
{
|
{
|
||||||
client::MyContext context;
|
client::MyContext context;
|
||||||
context.external_config = this->external_config();
|
context.external_config = this->external_config();
|
||||||
context.config = &this->config();
|
context.config = &this->config();
|
||||||
context.config_override = config_override;
|
context.config_override = config_override;
|
||||||
context.current_extruder_id = current_extruder_id;
|
context.current_extruder_id = current_extruder_id;
|
||||||
|
context.context_data = context_data;
|
||||||
return process_macro(templ, context);
|
return process_macro(templ, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
|
@ -12,6 +13,15 @@ namespace Slic3r {
|
||||||
class PlaceholderParser
|
class PlaceholderParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// Context to be shared during multiple executions of the PlaceholderParser.
|
||||||
|
// The context is kept external to the PlaceholderParser, so that the same PlaceholderParser
|
||||||
|
// may be called safely from multiple threads.
|
||||||
|
// In the future, the context may hold variables created and modified by the PlaceholderParser
|
||||||
|
// and shared between the PlaceholderParser::process() invocations.
|
||||||
|
struct ContextData {
|
||||||
|
std::mt19937 rng;
|
||||||
|
};
|
||||||
|
|
||||||
PlaceholderParser(const DynamicConfig *external_config = nullptr);
|
PlaceholderParser(const DynamicConfig *external_config = nullptr);
|
||||||
|
|
||||||
// Return a list of keys, which should be changed in m_config from rhs.
|
// Return a list of keys, which should be changed in m_config from rhs.
|
||||||
|
@ -41,7 +51,7 @@ public:
|
||||||
|
|
||||||
// Fill in the template using a macro processing language.
|
// Fill in the template using a macro processing language.
|
||||||
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
||||||
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr) const;
|
std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, ContextData *context = nullptr) const;
|
||||||
|
|
||||||
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
|
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
|
||||||
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
||||||
|
|
Loading…
Reference in a new issue