diff --git a/CHANGELOG.md b/CHANGELOG.md index b55d5fae6d..a33fcd53c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ Feature #7148: Optimize string literal lookup in mwscript Feature #7194: Ori to show texture paths Feature #7214: Searching in the in-game console + Task #7113: Move from std::atoi to std::from_char Task #7117: Replace boost::scoped_array with std::vector Task #7151: Do not use std::strerror to get errno error message diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 607e8e8c25..97789ebaab 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -6,6 +6,7 @@ #include #include +#include #include namespace @@ -68,9 +69,7 @@ namespace std::string type_str = "INVALID"; std::string func_str = Misc::StringUtils::format("INVALID=%s", rule.substr(1, 3)); - int func; - std::istringstream iss(rule.substr(2, 2)); - iss >> func; + int func = Misc::StringUtils::toNumeric(rule.substr(2, 2), 0); switch (type) { diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp index f6073ea512..0a8a4a2aa0 100644 --- a/apps/opencs/model/world/land.cpp +++ b/apps/opencs/model/world/land.cpp @@ -1,9 +1,10 @@ #include "land.hpp" #include -#include #include +#include + namespace ESM { class ESMReader; @@ -30,7 +31,7 @@ namespace CSMWorld if (mid == std::string::npos || id[0] != '#') throw std::runtime_error("Invalid Land ID"); - x = std::stoi(id.substr(1, mid - 1)); - y = std::stoi(id.substr(mid + 1)); + x = Misc::StringUtils::toNumeric(id.substr(1, mid - 1), 0); + y = Misc::StringUtils::toNumeric(id.substr(mid + 1), 0); } } diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp index 1ef8d9cad4..ecf370c282 100644 --- a/apps/opencs/model/world/landtexture.cpp +++ b/apps/opencs/model/world/landtexture.cpp @@ -1,10 +1,10 @@ #include "landtexture.hpp" #include -#include #include #include +#include namespace CSMWorld { @@ -29,7 +29,7 @@ namespace CSMWorld if (middle == std::string::npos || id[0] != 'L') throw std::runtime_error("Invalid LandTexture ID"); - plugin = std::stoi(id.substr(1, middle - 1)); - index = std::stoi(id.substr(middle + 1)); + plugin = Misc::StringUtils::toNumeric(id.substr(1, middle - 1), 0); + index = Misc::StringUtils::toNumeric(id.substr(middle + 1), 0); } } diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 1479e82286..a0bf5fb3b9 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "cell.hpp" #include "record.hpp" @@ -231,7 +232,7 @@ unsigned int CSMWorld::RefCollection::extractIdNum(std::string_view id) const if (separator == std::string::npos) throw std::runtime_error("invalid ref ID: " + std::string(id)); - return static_cast(std::stoi(std::string(id.substr(separator + 1)))); + return Misc::StringUtils::toNumeric(id.substr(separator + 1), 0); } int CSMWorld::RefCollection::getIntIndex(unsigned int id) const diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index dfac049acb..7e1e65674d 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -27,6 +27,8 @@ #include #include +#include + #include "../widget/scenetoolbar.hpp" #include "../widget/scenetooltexturebrush.hpp" @@ -360,7 +362,9 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe std::size_t hashlocation = mBrushTexture.find('#'); std::string mBrushTextureInt = mBrushTexture.substr(hashlocation + 1); - int brushInt = stoi(mBrushTexture.substr(hashlocation + 1)) + 1; // All indices are offset by +1 + + // All indices are offset by +1 + int brushInt = Misc::StringUtils::toNumeric(mBrushTexture.substr(hashlocation + 1), 0) + 1; int r = static_cast(mBrushSize) / 2; diff --git a/apps/openmw/mwdialogue/selectwrapper.cpp b/apps/openmw/mwdialogue/selectwrapper.cpp index fa33dcb8e9..94f7f73097 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -4,6 +4,7 @@ #include #include +#include #include namespace @@ -48,9 +49,7 @@ namespace MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() const { - int index = 0; - - std::istringstream(mSelect.mSelectRule.substr(2, 2)) >> index; + const int index = Misc::StringUtils::toNumeric(mSelect.mSelectRule.substr(2, 2), 0); switch (index) { diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 474615ab2d..a124f0b8ba 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -1,5 +1,8 @@ #include "formatting.hpp" +#include +#include + #include #include #include @@ -363,10 +366,9 @@ namespace MWGui::Formatting { if (attr.find("color") != attr.end()) { - unsigned int color; - std::stringstream ss; - ss << attr.at("color"); - ss >> std::hex >> color; + auto& colorString = attr.at("color"); + unsigned int color = 0; + std::from_chars(colorString.data(), colorString.data() + colorString.size(), color, 16); mTextStyle.mColour = MyGUI::Colour((color >> 16 & 0xFF) / 255.f, (color >> 8 & 0xFF) / 255.f, (color & 0xFF) / 255.f); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 817eaad535..f9dcbf9ce2 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -148,9 +149,7 @@ namespace MWGui void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender) { - std::istringstream iss(_sender->getUserString("price")); - int price; - iss >> price; + const int price = Misc::StringUtils::toNumeric(_sender->getUserString("price"), 0); MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 544d7b299b..b0d5a108bb 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -19,13 +19,12 @@ #include "character.hpp" -#include - #include #include #include #include #include +#include #include @@ -1006,23 +1005,23 @@ namespace MWMechanics std::string_view soundgen = evt.substr(10); // The event can optionally contain volume and pitch modifiers - float volume = 1.f, pitch = 1.f; + float volume = 1.0f; + float pitch = 1.0f; + if (soundgen.find(' ') != std::string::npos) { std::vector tokens; Misc::StringUtils::split(soundgen, tokens); soundgen = tokens[0]; + if (tokens.size() >= 2) { - std::stringstream stream; - stream << tokens[1]; - stream >> volume; + volume = Misc::StringUtils::toNumeric(tokens[1], volume); } + if (tokens.size() >= 3) { - std::stringstream stream; - stream << tokens[2]; - stream >> pitch; + pitch = Misc::StringUtils::toNumeric(tokens[2], pitch); } } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 8db390a6ea..26fecd93b9 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -121,14 +122,13 @@ namespace MWPhysics mCollisionWorld->setForceUpdateAllAabbs(false); // Check if a user decided to override a physics system FPS - const char* env = getenv("OPENMW_PHYSICS_FPS"); - if (env) + if (const char* env = getenv("OPENMW_PHYSICS_FPS")) { - float physFramerate = std::atof(env); - if (physFramerate > 0) + if (const auto physFramerate = Misc::StringUtils::toNumeric(env); + physFramerate.has_value() && *physFramerate > 0) { - mPhysicsDt = 1.f / physFramerate; - Log(Debug::Warning) << "Warning: using custom physics framerate (" << physFramerate << " FPS)."; + mPhysicsDt = 1.f / *physFramerate; + Log(Debug::Warning) << "Warning: using custom physics framerate (" << *physFramerate << " FPS)."; } } diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index fb04d11cc9..87fb705187 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -195,13 +196,19 @@ namespace MWRender int cubeSize = screenshotMapping == Planet ? screenshotW : screenshotW / 2; if (settingArgs.size() > 1) - screenshotW = std::min(10000, std::atoi(settingArgs[1].c_str())); + { + screenshotW = std::min(10000, Misc::StringUtils::toNumeric(settingArgs[1], 0)); + } if (settingArgs.size() > 2) - screenshotH = std::min(10000, std::atoi(settingArgs[2].c_str())); + { + screenshotH = std::min(10000, Misc::StringUtils::toNumeric(settingArgs[2], 0)); + } if (settingArgs.size() > 3) - cubeSize = std::min(5000, std::atoi(settingArgs[3].c_str())); + { + cubeSize = std::min(5000, Misc::StringUtils::toNumeric(settingArgs[3], 0)); + } bool rawCubemap = screenshotMapping == RawCubemap; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 6d9313e97f..5b819a0f6a 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -575,9 +576,12 @@ namespace MWScript return; } - char* end; - long key = strtol(effect.data(), &end, 10); - if (key < 0 || key > 32767 || *end != '\0') + long key; + + if (const auto k = ::Misc::StringUtils::toNumeric(effect.data()); + k.has_value() && *k >= 0 && *k <= 32767) + key = *k; + else key = ESM::MagicEffect::effectStringToId({ effect }); const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 673d499892..4794f569b5 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -8,6 +8,7 @@ #include "extensions.hpp" #include "parser.hpp" +#include #include namespace @@ -207,12 +208,8 @@ namespace Compiler TokenLoc loc(mLoc); mLoc.mLiteral.clear(); - std::istringstream stream(value); + cont = parser.parseInt(Misc::StringUtils::toNumeric(value, 0), loc, *this); - int intValue = 0; - stream >> intValue; - - cont = parser.parseInt(intValue, loc, *this); return true; } @@ -247,12 +244,8 @@ namespace Compiler TokenLoc loc(mLoc); mLoc.mLiteral.clear(); - std::istringstream stream(value); - - float floatValue = 0; - stream >> floatValue; + cont = parser.parseFloat(Misc::StringUtils::toNumeric(value, 0.0f), loc, *this); - cont = parser.parseFloat(floatValue, loc, *this); return true; } diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index 391ec58df4..4364ec1af5 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #ifdef _WIN32 @@ -314,7 +315,8 @@ int wrapApplication(int (*innerApplication)(int argc, char* argv[]), int argc, c int ret = 0; try { - if (const auto env = std::getenv("OPENMW_DISABLE_CRASH_CATCHER"); env == nullptr || std::atol(env) == 0) + if (const auto env = std::getenv("OPENMW_DISABLE_CRASH_CATCHER"); + env == nullptr || Misc::StringUtils::toNumeric(env, 0) == 0) { #if defined(_WIN32) const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.dmp"; diff --git a/components/esm4/formid.cpp b/components/esm4/formid.cpp index 7aab4ac6d6..c7655ce816 100644 --- a/components/esm4/formid.cpp +++ b/components/esm4/formid.cpp @@ -22,13 +22,10 @@ */ #include "formid.hpp" -#include -#include // LONG_MIN, LONG_MAX for gcc -#include // strtol -#include +#include #include - #include +#include namespace ESM4 { @@ -54,16 +51,13 @@ namespace ESM4 if (str.size() != 8) return false; - char* tmp; - errno = 0; - unsigned long val = strtol(str.c_str(), &tmp, 16); + unsigned long value = 0; - if (tmp == str.c_str() || *tmp != '\0' - || ((val == (unsigned long)LONG_MIN || val == (unsigned long)LONG_MAX) && errno == ERANGE)) + if (auto [_ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value, 16); ec != std::errc()) return false; if (id != nullptr) - *id = static_cast(val); + *id = static_cast(value); return true; } @@ -73,6 +67,11 @@ namespace ESM4 if (str.size() != 8) throw std::out_of_range("StringToFormId: incorrect string size"); - return static_cast(std::stoul(str, nullptr, 16)); + unsigned long value = 0; + + if (auto [_ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value, 16); ec != std::errc()) + throw std::invalid_argument("StringToFormId: string not a valid hexadecimal number"); + + return static_cast(value); } } diff --git a/components/fallback/fallback.cpp b/components/fallback/fallback.cpp index e0bf19111c..8711be33dc 100644 --- a/components/fallback/fallback.cpp +++ b/components/fallback/fallback.cpp @@ -1,8 +1,11 @@ #include "fallback.hpp" -#include +#include +#include #include +#include +#include namespace Fallback { @@ -27,31 +30,13 @@ namespace Fallback float Map::getFloat(std::string_view fall) { std::string_view fallback = getString(fall); - if (!fallback.empty()) - { - std::stringstream stream; - stream << fallback; - float number = 0.f; - stream >> number; - return number; - } - - return 0; + return Misc::StringUtils::toNumeric(fallback, 0.0f); } int Map::getInt(std::string_view fall) { std::string_view fallback = getString(fall); - if (!fallback.empty()) - { - std::stringstream stream; - stream << fallback; - int number = 0; - stream >> number; - return number; - } - - return 0; + return Misc::StringUtils::toNumeric(fallback, 0); } bool Map::getBool(std::string_view fall) @@ -62,30 +47,29 @@ namespace Fallback osg::Vec4f Map::getColour(std::string_view fall) { - std::string_view sum = getString(fall); + const std::string_view sum = getString(fall); + if (!sum.empty()) { - try + std::vector ret; + Misc::StringUtils::split(sum, ret, ","); + + if (ret.size() == 3) { - std::string ret[3]; - unsigned int j = 0; - for (unsigned int i = 0; i < sum.length(); ++i) + const auto r = Misc::StringUtils::toNumeric(ret[0]); + const auto g = Misc::StringUtils::toNumeric(ret[1]); + const auto b = Misc::StringUtils::toNumeric(ret[2]); + + if (r.has_value() && g.has_value() && b.has_value()) { - if (sum[i] == ',') - j++; - else if (sum[i] != ' ') - ret[j] += sum[i]; + return osg::Vec4f(*r / 255.0f, *g / 255.0f, *b / 255.0f, 1.0f); } - return osg::Vec4f(std::stoi(ret[0]) / 255.f, std::stoi(ret[1]) / 255.f, std::stoi(ret[2]) / 255.f, 1.f); - } - catch (const std::invalid_argument&) - { - Log(Debug::Error) << "Error: '" << fall << "' setting value (" << sum - << ") is not a valid color, using middle gray as a fallback"; } + + Log(Debug::Error) << "Error: '" << fall << "' setting value (" << sum + << ") is not a valid color, using middle gray as a fallback"; } return osg::Vec4f(0.5f, 0.5f, 0.5f, 1.f); } - } diff --git a/components/misc/color.cpp b/components/misc/color.cpp index 2891004749..5b0278570a 100644 --- a/components/misc/color.cpp +++ b/components/misc/color.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace Misc { diff --git a/components/misc/strings/conversion.hpp b/components/misc/strings/conversion.hpp index 50df405610..bac4ab49b7 100644 --- a/components/misc/strings/conversion.hpp +++ b/components/misc/strings/conversion.hpp @@ -1,7 +1,19 @@ #ifndef COMPONENTS_MISC_STRINGS_CONVERSION_H #define COMPONENTS_MISC_STRINGS_CONVERSION_H +#include +#include #include +#include + +#if !(defined(_MSC_VER) && (_MSC_VER >= 1924)) && !(defined(__GNUC__) && __GNUC__ >= 11) || defined(__clang__) \ + || defined(__apple_build_version__) + +#include +#include +#include + +#endif namespace Misc::StringUtils { @@ -45,6 +57,78 @@ namespace Misc::StringUtils { return { str.begin(), str.end() }; // Undefined behavior if the contents of "char" aren't UTF8 or ASCII. } + + template + inline std::optional toNumeric(std::string_view s) + { + T result{}; + auto [ptr, ec]{ std::from_chars(s.data(), s.data() + s.size(), result) }; + + if (ec == std::errc()) + { + return result; + } + + return std::nullopt; + } + + template + inline T toNumeric(std::string_view s, T defaultValue) + { + if (auto numeric = toNumeric(s)) + { + return *numeric; + } + + return defaultValue; + } + + // support for std::from_chars as of 2023-02-27 + // - Visual Studio 2019 version 16.4 (1924) + // - GCC 11 + // - Clang does not support floating points yet + // - Apples Clang does not support floating points yet + +#if !(defined(_MSC_VER) && (_MSC_VER >= 1924)) && !(defined(__GNUC__) && __GNUC__ >= 11) || defined(__clang__) \ + || defined(__apple_build_version__) + template <> + inline std::optional toNumeric(std::string_view s) + { + if (!s.empty()) + { + std::istringstream iss(s.data()); + iss.imbue(std::locale::classic()); + + float value; + + if (iss >> value) + { + return value; + } + } + + return std::nullopt; + } + + template <> + inline std::optional toNumeric(std::string_view s) + { + if (!s.empty()) + { + std::istringstream iss(s.data()); + iss.imbue(std::locale::classic()); + + double value; + + if (iss >> value) + { + return value; + } + } + + return std::nullopt; + } +#endif } -#endif // COMPONENTS_MISC_STRINGS_CONVERSION_H \ No newline at end of file +#endif // COMPONENTS_MISC_STRINGS_CONVERSION_H diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index c547d0f9f0..6e9e6c1ed4 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -184,7 +185,7 @@ namespace Resource size_t spacePos = line.find_last_of(' '); double time = 0.0; if (spacePos != std::string::npos && spacePos + 1 < line.size()) - time = std::stod(line.substr(spacePos + 1)); + time = Misc::StringUtils::toNumeric(line.substr(spacePos + 1), time); return time; } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 4184786aea..a6a16d4337 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -308,8 +309,8 @@ namespace Resource if (descriptionParts.at(0) == "alphatest") { osg::AlphaFunc::ComparisonFunction mode = getTestMode(descriptionParts.at(1)); - osg::ref_ptr alphaFunc( - new osg::AlphaFunc(mode, std::stod(descriptionParts.at(2)))); + osg::ref_ptr alphaFunc(new osg::AlphaFunc( + mode, Misc::StringUtils::toNumeric(descriptionParts.at(2), 0.0f))); node.getStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); } } diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index c5dd9a7837..b389c63b6a 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -1,43 +1,84 @@ #include "settings.hpp" #include "parser.hpp" -#include #include #include #include #include +#if !(defined(_MSC_VER) && (_MSC_VER >= 1924)) && !(defined(__GNUC__) && __GNUC__ >= 11) || defined(__clang__) \ + || defined(__apple_build_version__) + +#include +#include +#include + +#endif + #include #include +#include namespace Settings { namespace { template - auto parseIntegralNumber(const std::string& value, std::string_view setting, std::string_view category) + T parseNumberFromSetting(const std::string& value, std::string_view setting, std::string_view category) { T number{}; + const auto result = std::from_chars(value.data(), value.data() + value.size(), number); if (result.ec != std::errc()) + { throw std::system_error(std::make_error_code(result.ec), "Failed to parse number from setting [" + std::string(category) + "] " + std::string(setting) + " value \"" + value + "\""); + } + return number; } - template - auto parseFloatingPointNumber(const std::string& value, std::string_view setting, std::string_view category) +#if !(defined(_MSC_VER) && (_MSC_VER >= 1924)) && !(defined(__GNUC__) && __GNUC__ >= 11) || defined(__clang__) \ + || defined(__apple_build_version__) + template <> + float parseNumberFromSetting( + const std::string& value, std::string_view setting, std::string_view category) { - std::stringstream stream(value); - T number{}; - stream >> number; - if (stream.bad()) + std::istringstream iss(value); + iss.imbue(std::locale::classic()); + + float floatValue = 0.0f; + + if (!(iss >> floatValue)) + { throw std::system_error(errno, std::generic_category(), "Failed to parse number from setting [" + std::string(category) + "] " + std::string(setting) + " value \"" + value + "\""); - return number; + } + + return floatValue; } + + template <> + double parseNumberFromSetting( + const std::string& value, std::string_view setting, std::string_view category) + { + std::istringstream iss(value); + iss.imbue(std::locale::classic()); + + double doubleValue = 0.0; + + if (!(iss >> doubleValue)) + { + throw std::system_error(errno, std::generic_category(), + "Failed to parse number from setting [" + std::string(category) + "] " + std::string(setting) + + " value \"" + value + "\""); + } + + return doubleValue; + } +#endif } CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap(); @@ -139,27 +180,27 @@ namespace Settings float Manager::getFloat(std::string_view setting, std::string_view category) { - return parseFloatingPointNumber(getString(setting, category), setting, category); + return parseNumberFromSetting(getString(setting, category), setting, category); } double Manager::getDouble(std::string_view setting, std::string_view category) { - return parseFloatingPointNumber(getString(setting, category), setting, category); + return parseNumberFromSetting(getString(setting, category), setting, category); } int Manager::getInt(std::string_view setting, std::string_view category) { - return parseIntegralNumber(getString(setting, category), setting, category); + return parseNumberFromSetting(getString(setting, category), setting, category); } std::uint64_t Manager::getUInt64(std::string_view setting, std::string_view category) { - return parseIntegralNumber(getString(setting, category), setting, category); + return parseNumberFromSetting(getString(setting, category), setting, category); } std::size_t Manager::getSize(std::string_view setting, std::string_view category) { - return parseIntegralNumber(getString(setting, category), setting, category); + return parseNumberFromSetting(getString(setting, category), setting, category); } bool Manager::getBool(std::string_view setting, std::string_view category) @@ -171,23 +212,44 @@ namespace Settings osg::Vec2f Manager::getVector2(std::string_view setting, std::string_view category) { const std::string& value = getString(setting, category); - std::stringstream stream(value); - float x, y; - stream >> x >> y; - if (stream.fail()) - throw std::runtime_error(std::string("Can't parse 2d vector: " + value)); - return { x, y }; + + std::vector components; + Misc::StringUtils::split(value, components); + + if (components.size() == 2) + { + auto x = Misc::StringUtils::toNumeric(components[0]); + auto y = Misc::StringUtils::toNumeric(components[1]); + + if (x && y) + { + return { x.value(), y.value() }; + } + } + + throw std::runtime_error(std::string("Can't parse 2d vector: " + value)); } osg::Vec3f Manager::getVector3(std::string_view setting, std::string_view category) { const std::string& value = getString(setting, category); - std::stringstream stream(value); - float x, y, z; - stream >> x >> y >> z; - if (stream.fail()) - throw std::runtime_error(std::string("Can't parse 3d vector: " + value)); - return { x, y, z }; + + std::vector components; + Misc::StringUtils::split(value, components); + + if (components.size() == 3) + { + auto x = Misc::StringUtils::toNumeric(components[0]); + auto y = Misc::StringUtils::toNumeric(components[1]); + auto z = Misc::StringUtils::toNumeric(components[2]); + + if (x && y && z) + { + return { x.value(), y.value(), z.value() }; + } + } + + throw std::runtime_error(std::string("Can't parse 3d vector: " + value)); } void Manager::setString(std::string_view setting, std::string_view category, const std::string& value) diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 5b9b79e0f8..fd5da26eb3 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -90,7 +91,7 @@ namespace Shader size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length(); size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart); std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart); - lineNumber = std::stoi(lineNumberString) - 1; + lineNumber = Misc::StringUtils::toNumeric(lineNumberString, 2) - 1; } else { @@ -159,7 +160,7 @@ namespace Shader size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length(); size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart); std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart); - lineNumber = std::stoi(lineNumberString) - 1; + lineNumber = Misc::StringUtils::toNumeric(lineNumberString, 2) - 1; } else { @@ -239,7 +240,7 @@ namespace Shader size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length(); size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart); std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart); - lineNumber = std::stoi(lineNumberString); + lineNumber = Misc::StringUtils::toNumeric(lineNumberString, 2); } else { diff --git a/components/widgets/numericeditbox.cpp b/components/widgets/numericeditbox.cpp index be031e4d83..6cdc13b1d6 100644 --- a/components/widgets/numericeditbox.cpp +++ b/components/widgets/numericeditbox.cpp @@ -2,6 +2,8 @@ #include "numericeditbox.hpp" +#include + namespace Gui { @@ -22,27 +24,25 @@ namespace Gui void NumericEditBox::onEditTextChange(MyGUI::EditBox* sender) { - std::string newCaption = sender->getCaption(); + const std::string newCaption = sender->getCaption(); + if (newCaption.empty()) { return; } - try + if (const auto conversion = Misc::StringUtils::toNumeric(newCaption)) { - mValue = std::stoi(newCaption); - int capped = std::clamp(mValue, mMinValue, mMaxValue); + mValue = conversion.value(); + + const int capped = std::clamp(mValue, mMinValue, mMaxValue); if (capped != mValue) { mValue = capped; setCaption(MyGUI::utility::toString(mValue)); } } - catch (const std::invalid_argument&) - { - setCaption(MyGUI::utility::toString(mValue)); - } - catch (const std::out_of_range&) + else { setCaption(MyGUI::utility::toString(mValue)); }