#include "LocalesUtils.hpp" #ifdef _WIN32 #include #endif #include #include #include #include namespace Slic3r { // How many setters this thread holds, so the ones nested in another can skip // setlocale, which takes a lock the whole process shares on Windows. static thread_local int s_numeric_locale_depth = 0; CNumericLocalesSetter::CNumericLocalesSetter() { // Nested in another setter on this thread, whose "C" the separator check // confirms is still set. if (s_numeric_locale_depth > 0 && is_decimal_separator_point()) { m_nested = true; ++ s_numeric_locale_depth; return; } #ifdef _WIN32 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr); std::setlocale(LC_NUMERIC, "C"); #elif __APPLE__ m_original_locale = uselocale((locale_t)0); m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale); uselocale(m_new_locale); #else // linux / BSD m_original_locale = uselocale((locale_t)0); m_new_locale = duplocale(m_original_locale); m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale); uselocale(m_new_locale); #endif // Counted last, since the destructor does not run for a constructor that throws. ++ s_numeric_locale_depth; } CNumericLocalesSetter::~CNumericLocalesSetter() { -- s_numeric_locale_depth; if (m_nested) return; #ifdef _WIN32 std::setlocale(LC_NUMERIC, m_orig_numeric_locale.data()); #else uselocale(m_original_locale); freelocale(m_new_locale); #endif } bool is_decimal_separator_point() { char str[5] = ""; sprintf(str, "%.1f", 0.5f); return str[1] == '.'; } double string_to_double_decimal_point(const std::string_view str, size_t* pos /* = nullptr*/) { double out = 0.; size_t p = fast_float::from_chars(str.data(), str.data() + str.size(), out).ptr - str.data(); if (pos) *pos = p; return out; } std::string float_to_string_decimal_point(double value, int precision/* = -1*/) { // Our Windows build server fully supports C++17 std::to_chars. Let's use it. // Other platforms are behind, fall back to slow stringstreams for now. #ifdef _WIN32 constexpr size_t SIZE = 20; char out[SIZE] = ""; std::to_chars_result res; if (precision >=0) res = std::to_chars(out, out+SIZE, value, std::chars_format::fixed, precision); else res = std::to_chars(out, out+SIZE, value, std::chars_format::general, 6); if (res.ec == std::errc::value_too_large) throw std::invalid_argument("float_to_string_decimal_point conversion failed."); return std::string(out, res.ptr - out); #else std::stringstream buf; if (precision >= 0) buf << std::fixed << std::setprecision(precision); buf << value; return buf.str(); #endif } } // namespace Slic3r