Move from std::atoi to std::from_char

depth-refraction
Shi Han 2 years ago committed by psi29a
parent ca13aa6332
commit a90e3b8c3b

@ -66,6 +66,7 @@
Feature #7148: Optimize string literal lookup in mwscript Feature #7148: Optimize string literal lookup in mwscript
Feature #7194: Ori to show texture paths Feature #7194: Ori to show texture paths
Feature #7214: Searching in the in-game console 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 #7117: Replace boost::scoped_array with std::vector
Task #7151: Do not use std::strerror to get errno error message Task #7151: Do not use std::strerror to get errno error message

@ -6,6 +6,7 @@
#include <components/esm3/cellstate.hpp> #include <components/esm3/cellstate.hpp>
#include <components/esm3/esmreader.hpp> #include <components/esm3/esmreader.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/misc/strings/format.hpp> #include <components/misc/strings/format.hpp>
namespace namespace
@ -68,9 +69,7 @@ namespace
std::string type_str = "INVALID"; std::string type_str = "INVALID";
std::string func_str = Misc::StringUtils::format("INVALID=%s", rule.substr(1, 3)); std::string func_str = Misc::StringUtils::format("INVALID=%s", rule.substr(1, 3));
int func; int func = Misc::StringUtils::toNumeric<int>(rule.substr(2, 2), 0);
std::istringstream iss(rule.substr(2, 2));
iss >> func;
switch (type) switch (type)
{ {

@ -1,9 +1,10 @@
#include "land.hpp" #include "land.hpp"
#include <sstream> #include <sstream>
#include <stddef.h>
#include <stdexcept> #include <stdexcept>
#include <components/misc/strings/conversion.hpp>
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -30,7 +31,7 @@ namespace CSMWorld
if (mid == std::string::npos || id[0] != '#') if (mid == std::string::npos || id[0] != '#')
throw std::runtime_error("Invalid Land ID"); throw std::runtime_error("Invalid Land ID");
x = std::stoi(id.substr(1, mid - 1)); x = Misc::StringUtils::toNumeric<int>(id.substr(1, mid - 1), 0);
y = std::stoi(id.substr(mid + 1)); y = Misc::StringUtils::toNumeric<int>(id.substr(mid + 1), 0);
} }
} }

@ -1,10 +1,10 @@
#include "landtexture.hpp" #include "landtexture.hpp"
#include <sstream> #include <sstream>
#include <stddef.h>
#include <stdexcept> #include <stdexcept>
#include <components/esm3/esmreader.hpp> #include <components/esm3/esmreader.hpp>
#include <components/misc/strings/conversion.hpp>
namespace CSMWorld namespace CSMWorld
{ {
@ -29,7 +29,7 @@ namespace CSMWorld
if (middle == std::string::npos || id[0] != 'L') if (middle == std::string::npos || id[0] != 'L')
throw std::runtime_error("Invalid LandTexture ID"); throw std::runtime_error("Invalid LandTexture ID");
plugin = std::stoi(id.substr(1, middle - 1)); plugin = Misc::StringUtils::toNumeric<int>(id.substr(1, middle - 1), 0);
index = std::stoi(id.substr(middle + 1)); index = Misc::StringUtils::toNumeric<int>(id.substr(middle + 1), 0);
} }
} }

@ -9,6 +9,7 @@
#include <components/esm3/cellref.hpp> #include <components/esm3/cellref.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/misc/strings/conversion.hpp>
#include "cell.hpp" #include "cell.hpp"
#include "record.hpp" #include "record.hpp"
@ -231,7 +232,7 @@ unsigned int CSMWorld::RefCollection::extractIdNum(std::string_view id) const
if (separator == std::string::npos) if (separator == std::string::npos)
throw std::runtime_error("invalid ref ID: " + std::string(id)); throw std::runtime_error("invalid ref ID: " + std::string(id));
return static_cast<unsigned int>(std::stoi(std::string(id.substr(separator + 1)))); return Misc::StringUtils::toNumeric<unsigned int>(id.substr(separator + 1), 0);
} }
int CSMWorld::RefCollection::getIntIndex(unsigned int id) const int CSMWorld::RefCollection::getIntIndex(unsigned int id) const

@ -27,6 +27,8 @@
#include <apps/opencs/view/render/terrainselection.hpp> #include <apps/opencs/view/render/terrainselection.hpp>
#include <apps/opencs/view/widget/scenetool.hpp> #include <apps/opencs/view/widget/scenetool.hpp>
#include <components/misc/strings/conversion.hpp>
#include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolbar.hpp"
#include "../widget/scenetooltexturebrush.hpp" #include "../widget/scenetooltexturebrush.hpp"
@ -360,7 +362,9 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
std::size_t hashlocation = mBrushTexture.find('#'); std::size_t hashlocation = mBrushTexture.find('#');
std::string mBrushTextureInt = mBrushTexture.substr(hashlocation + 1); 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<int>(mBrushTexture.substr(hashlocation + 1), 0) + 1;
int r = static_cast<float>(mBrushSize) / 2; int r = static_cast<float>(mBrushSize) / 2;

@ -4,6 +4,7 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <components/misc/strings/conversion.hpp>
#include <components/misc/strings/lower.hpp> #include <components/misc/strings/lower.hpp>
namespace namespace
@ -48,9 +49,7 @@ namespace
MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() const MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() const
{ {
int index = 0; const int index = Misc::StringUtils::toNumeric<int>(mSelect.mSelectRule.substr(2, 2), 0);
std::istringstream(mSelect.mSelectRule.substr(2, 2)) >> index;
switch (index) switch (index)
{ {

@ -1,5 +1,8 @@
#include "formatting.hpp" #include "formatting.hpp"
#include <charconv>
#include <system_error>
#include <MyGUI_EditBox.h> #include <MyGUI_EditBox.h>
#include <MyGUI_EditText.h> #include <MyGUI_EditText.h>
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
@ -363,10 +366,9 @@ namespace MWGui::Formatting
{ {
if (attr.find("color") != attr.end()) if (attr.find("color") != attr.end())
{ {
unsigned int color; auto& colorString = attr.at("color");
std::stringstream ss; unsigned int color = 0;
ss << attr.at("color"); std::from_chars(colorString.data(), colorString.data() + colorString.size(), color, 16);
ss >> std::hex >> color;
mTextStyle.mColour mTextStyle.mColour
= MyGUI::Colour((color >> 16 & 0xFF) / 255.f, (color >> 8 & 0xFF) / 255.f, (color & 0xFF) / 255.f); = MyGUI::Colour((color >> 16 & 0xFF) / 255.f, (color >> 8 & 0xFF) / 255.f, (color & 0xFF) / 255.f);

@ -6,6 +6,7 @@
#include <components/esm3/loadcrea.hpp> #include <components/esm3/loadcrea.hpp>
#include <components/esm3/loadgmst.hpp> #include <components/esm3/loadgmst.hpp>
#include <components/misc/strings/conversion.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -148,9 +149,7 @@ namespace MWGui
void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender) void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender)
{ {
std::istringstream iss(_sender->getUserString("price")); const int price = Misc::StringUtils::toNumeric<int>(_sender->getUserString("price"), 0);
int price;
iss >> price;
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);

@ -19,13 +19,12 @@
#include "character.hpp" #include "character.hpp"
#include <sstream>
#include <components/esm/records.hpp> #include <components/esm/records.hpp>
#include <components/misc/mathutil.hpp> #include <components/misc/mathutil.hpp>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -1006,23 +1005,23 @@ namespace MWMechanics
std::string_view soundgen = evt.substr(10); std::string_view soundgen = evt.substr(10);
// The event can optionally contain volume and pitch modifiers // 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) if (soundgen.find(' ') != std::string::npos)
{ {
std::vector<std::string_view> tokens; std::vector<std::string_view> tokens;
Misc::StringUtils::split(soundgen, tokens); Misc::StringUtils::split(soundgen, tokens);
soundgen = tokens[0]; soundgen = tokens[0];
if (tokens.size() >= 2) if (tokens.size() >= 2)
{ {
std::stringstream stream; volume = Misc::StringUtils::toNumeric<float>(tokens[1], volume);
stream << tokens[1];
stream >> volume;
} }
if (tokens.size() >= 3) if (tokens.size() >= 3)
{ {
std::stringstream stream; pitch = Misc::StringUtils::toNumeric<float>(tokens[2], pitch);
stream << tokens[2];
stream >> pitch;
} }
} }

@ -24,6 +24,7 @@
#include <components/esm3/loadmgef.hpp> #include <components/esm3/loadmgef.hpp>
#include <components/misc/convert.hpp> #include <components/misc/convert.hpp>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/resource/bulletshapemanager.hpp> #include <components/resource/bulletshapemanager.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -121,14 +122,13 @@ namespace MWPhysics
mCollisionWorld->setForceUpdateAllAabbs(false); mCollisionWorld->setForceUpdateAllAabbs(false);
// Check if a user decided to override a physics system FPS // Check if a user decided to override a physics system FPS
const char* env = getenv("OPENMW_PHYSICS_FPS"); if (const char* env = getenv("OPENMW_PHYSICS_FPS"))
if (env)
{ {
float physFramerate = std::atof(env); if (const auto physFramerate = Misc::StringUtils::toNumeric<float>(env);
if (physFramerate > 0) physFramerate.has_value() && *physFramerate > 0)
{ {
mPhysicsDt = 1.f / physFramerate; mPhysicsDt = 1.f / *physFramerate;
Log(Debug::Warning) << "Warning: using custom physics framerate (" << physFramerate << " FPS)."; Log(Debug::Warning) << "Warning: using custom physics framerate (" << *physFramerate << " FPS).";
} }
} }

@ -9,6 +9,7 @@
#include <osg/TextureCubeMap> #include <osg/TextureCubeMap>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/depth.hpp> #include <components/sceneutil/depth.hpp>
@ -195,13 +196,19 @@ namespace MWRender
int cubeSize = screenshotMapping == Planet ? screenshotW : screenshotW / 2; int cubeSize = screenshotMapping == Planet ? screenshotW : screenshotW / 2;
if (settingArgs.size() > 1) if (settingArgs.size() > 1)
screenshotW = std::min(10000, std::atoi(settingArgs[1].c_str())); {
screenshotW = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[1], 0));
}
if (settingArgs.size() > 2) if (settingArgs.size() > 2)
screenshotH = std::min(10000, std::atoi(settingArgs[2].c_str())); {
screenshotH = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[2], 0));
}
if (settingArgs.size() > 3) if (settingArgs.size() > 3)
cubeSize = std::min(5000, std::atoi(settingArgs[3].c_str())); {
cubeSize = std::min(5000, Misc::StringUtils::toNumeric<int>(settingArgs[3], 0));
}
bool rawCubemap = screenshotMapping == RawCubemap; bool rawCubemap = screenshotMapping == RawCubemap;

@ -45,6 +45,7 @@
#include <components/esm3/loadweap.hpp> #include <components/esm3/loadweap.hpp>
#include <components/files/conversion.hpp> #include <components/files/conversion.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -575,9 +576,12 @@ namespace MWScript
return; return;
} }
char* end; long key;
long key = strtol(effect.data(), &end, 10);
if (key < 0 || key > 32767 || *end != '\0') if (const auto k = ::Misc::StringUtils::toNumeric<long>(effect.data());
k.has_value() && *k >= 0 && *k <= 32767)
key = *k;
else
key = ESM::MagicEffect::effectStringToId({ effect }); key = ESM::MagicEffect::effectStringToId({ effect });
const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);

@ -8,6 +8,7 @@
#include "extensions.hpp" #include "extensions.hpp"
#include "parser.hpp" #include "parser.hpp"
#include <components/misc/strings/conversion.hpp>
#include <components/misc/strings/lower.hpp> #include <components/misc/strings/lower.hpp>
namespace namespace
@ -207,12 +208,8 @@ namespace Compiler
TokenLoc loc(mLoc); TokenLoc loc(mLoc);
mLoc.mLiteral.clear(); mLoc.mLiteral.clear();
std::istringstream stream(value); cont = parser.parseInt(Misc::StringUtils::toNumeric<int>(value, 0), loc, *this);
int intValue = 0;
stream >> intValue;
cont = parser.parseInt(intValue, loc, *this);
return true; return true;
} }
@ -247,12 +244,8 @@ namespace Compiler
TokenLoc loc(mLoc); TokenLoc loc(mLoc);
mLoc.mLiteral.clear(); mLoc.mLiteral.clear();
std::istringstream stream(value); cont = parser.parseFloat(Misc::StringUtils::toNumeric<float>(value, 0.0f), loc, *this);
float floatValue = 0;
stream >> floatValue;
cont = parser.parseFloat(floatValue, loc, *this);
return true; return true;
} }

@ -10,6 +10,7 @@
#include <components/crashcatcher/crashcatcher.hpp> #include <components/crashcatcher/crashcatcher.hpp>
#include <components/files/conversion.hpp> #include <components/files/conversion.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/misc/strings/lower.hpp> #include <components/misc/strings/lower.hpp>
#ifdef _WIN32 #ifdef _WIN32
@ -314,7 +315,8 @@ int wrapApplication(int (*innerApplication)(int argc, char* argv[]), int argc, c
int ret = 0; int ret = 0;
try 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<int>(env, 0) == 0)
{ {
#if defined(_WIN32) #if defined(_WIN32)
const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.dmp"; const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.dmp";

@ -22,13 +22,10 @@
*/ */
#include "formid.hpp" #include "formid.hpp"
#include <algorithm> #include <charconv>
#include <climits> // LONG_MIN, LONG_MAX for gcc
#include <cstdlib> // strtol
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <system_error>
namespace ESM4 namespace ESM4
{ {
@ -54,16 +51,13 @@ namespace ESM4
if (str.size() != 8) if (str.size() != 8)
return false; return false;
char* tmp; unsigned long value = 0;
errno = 0;
unsigned long val = strtol(str.c_str(), &tmp, 16);
if (tmp == str.c_str() || *tmp != '\0' if (auto [_ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value, 16); ec != std::errc())
|| ((val == (unsigned long)LONG_MIN || val == (unsigned long)LONG_MAX) && errno == ERANGE))
return false; return false;
if (id != nullptr) if (id != nullptr)
*id = static_cast<FormId>(val); *id = static_cast<FormId>(value);
return true; return true;
} }
@ -73,6 +67,11 @@ namespace ESM4
if (str.size() != 8) if (str.size() != 8)
throw std::out_of_range("StringToFormId: incorrect string size"); throw std::out_of_range("StringToFormId: incorrect string size");
return static_cast<FormId>(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<FormId>(value);
} }
} }

@ -1,8 +1,11 @@
#include "fallback.hpp" #include "fallback.hpp"
#include <sstream> #include <string>
#include <vector>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
namespace Fallback namespace Fallback
{ {
@ -27,31 +30,13 @@ namespace Fallback
float Map::getFloat(std::string_view fall) float Map::getFloat(std::string_view fall)
{ {
std::string_view fallback = getString(fall); std::string_view fallback = getString(fall);
if (!fallback.empty()) return Misc::StringUtils::toNumeric<float>(fallback, 0.0f);
{
std::stringstream stream;
stream << fallback;
float number = 0.f;
stream >> number;
return number;
}
return 0;
} }
int Map::getInt(std::string_view fall) int Map::getInt(std::string_view fall)
{ {
std::string_view fallback = getString(fall); std::string_view fallback = getString(fall);
if (!fallback.empty()) return Misc::StringUtils::toNumeric<int>(fallback, 0);
{
std::stringstream stream;
stream << fallback;
int number = 0;
stream >> number;
return number;
}
return 0;
} }
bool Map::getBool(std::string_view fall) bool Map::getBool(std::string_view fall)
@ -62,30 +47,29 @@ namespace Fallback
osg::Vec4f Map::getColour(std::string_view fall) osg::Vec4f Map::getColour(std::string_view fall)
{ {
std::string_view sum = getString(fall); const std::string_view sum = getString(fall);
if (!sum.empty()) if (!sum.empty())
{ {
try std::vector<std::string> ret;
Misc::StringUtils::split(sum, ret, ",");
if (ret.size() == 3)
{ {
std::string ret[3]; const auto r = Misc::StringUtils::toNumeric<float>(ret[0]);
unsigned int j = 0; const auto g = Misc::StringUtils::toNumeric<float>(ret[1]);
for (unsigned int i = 0; i < sum.length(); ++i) const auto b = Misc::StringUtils::toNumeric<float>(ret[2]);
if (r.has_value() && g.has_value() && b.has_value())
{ {
if (sum[i] == ',') return osg::Vec4f(*r / 255.0f, *g / 255.0f, *b / 255.0f, 1.0f);
j++;
else if (sum[i] != ' ')
ret[j] += sum[i];
} }
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 Log(Debug::Error) << "Error: '" << fall << "' setting value (" << sum
<< ") is not a valid color, using middle gray as a fallback"; << ") is not a valid color, using middle gray as a fallback";
} }
}
return osg::Vec4f(0.5f, 0.5f, 0.5f, 1.f); return osg::Vec4f(0.5f, 0.5f, 0.5f, 1.f);
} }
} }

@ -4,6 +4,7 @@
#include <array> #include <array>
#include <charconv> #include <charconv>
#include <sstream> #include <sstream>
#include <system_error>
namespace Misc namespace Misc
{ {

@ -1,7 +1,19 @@
#ifndef COMPONENTS_MISC_STRINGS_CONVERSION_H #ifndef COMPONENTS_MISC_STRINGS_CONVERSION_H
#define COMPONENTS_MISC_STRINGS_CONVERSION_H #define COMPONENTS_MISC_STRINGS_CONVERSION_H
#include <charconv>
#include <optional>
#include <string> #include <string>
#include <system_error>
#if !(defined(_MSC_VER) && (_MSC_VER >= 1924)) && !(defined(__GNUC__) && __GNUC__ >= 11) || defined(__clang__) \
|| defined(__apple_build_version__)
#include <ios>
#include <locale>
#include <sstream>
#endif
namespace Misc::StringUtils 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. return { str.begin(), str.end() }; // Undefined behavior if the contents of "char" aren't UTF8 or ASCII.
} }
template <typename T>
inline std::optional<T> 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 <typename T>
inline T toNumeric(std::string_view s, T defaultValue)
{
if (auto numeric = toNumeric<T>(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<float> toNumeric<float>(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<double> toNumeric<double>(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 #endif // COMPONENTS_MISC_STRINGS_CONVERSION_H

@ -10,6 +10,7 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/pathhelpers.hpp> #include <components/misc/pathhelpers.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
#include <components/sceneutil/keyframe.hpp> #include <components/sceneutil/keyframe.hpp>
#include <components/sceneutil/osgacontroller.hpp> #include <components/sceneutil/osgacontroller.hpp>
@ -184,7 +185,7 @@ namespace Resource
size_t spacePos = line.find_last_of(' '); size_t spacePos = line.find_last_of(' ');
double time = 0.0; double time = 0.0;
if (spacePos != std::string::npos && spacePos + 1 < line.size()) if (spacePos != std::string::npos && spacePos + 1 < line.size())
time = std::stod(line.substr(spacePos + 1)); time = Misc::StringUtils::toNumeric<double>(line.substr(spacePos + 1), time);
return time; return time;
} }

@ -29,6 +29,7 @@
#include <components/misc/osguservalues.hpp> #include <components/misc/osguservalues.hpp>
#include <components/misc/pathhelpers.hpp> #include <components/misc/pathhelpers.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -308,8 +309,8 @@ namespace Resource
if (descriptionParts.at(0) == "alphatest") if (descriptionParts.at(0) == "alphatest")
{ {
osg::AlphaFunc::ComparisonFunction mode = getTestMode(descriptionParts.at(1)); osg::AlphaFunc::ComparisonFunction mode = getTestMode(descriptionParts.at(1));
osg::ref_ptr<osg::AlphaFunc> alphaFunc( osg::ref_ptr<osg::AlphaFunc> alphaFunc(new osg::AlphaFunc(
new osg::AlphaFunc(mode, std::stod(descriptionParts.at(2)))); mode, Misc::StringUtils::toNumeric<float>(descriptionParts.at(2), 0.0f)));
node.getStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); node.getStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);
} }
} }

@ -1,43 +1,84 @@
#include "settings.hpp" #include "settings.hpp"
#include "parser.hpp" #include "parser.hpp"
#include <cerrno>
#include <charconv> #include <charconv>
#include <filesystem> #include <filesystem>
#include <sstream> #include <sstream>
#include <system_error> #include <system_error>
#if !(defined(_MSC_VER) && (_MSC_VER >= 1924)) && !(defined(__GNUC__) && __GNUC__ >= 11) || defined(__clang__) \
|| defined(__apple_build_version__)
#include <cerrno>
#include <ios>
#include <locale>
#endif
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
namespace Settings namespace Settings
{ {
namespace namespace
{ {
template <class T> template <class T>
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{}; T number{};
const auto result = std::from_chars(value.data(), value.data() + value.size(), number); const auto result = std::from_chars(value.data(), value.data() + value.size(), number);
if (result.ec != std::errc()) if (result.ec != std::errc())
{
throw std::system_error(std::make_error_code(result.ec), throw std::system_error(std::make_error_code(result.ec),
"Failed to parse number from setting [" + std::string(category) + "] " + std::string(setting) "Failed to parse number from setting [" + std::string(category) + "] " + std::string(setting)
+ " value \"" + value + "\""); + " value \"" + value + "\"");
}
return number; return number;
} }
template <class T> #if !(defined(_MSC_VER) && (_MSC_VER >= 1924)) && !(defined(__GNUC__) && __GNUC__ >= 11) || defined(__clang__) \
auto parseFloatingPointNumber(const std::string& value, std::string_view setting, std::string_view category) || defined(__apple_build_version__)
template <>
float parseNumberFromSetting<float>(
const std::string& value, std::string_view setting, std::string_view category)
{
std::istringstream iss(value);
iss.imbue(std::locale::classic());
float floatValue = 0.0f;
if (!(iss >> floatValue))
{ {
std::stringstream stream(value);
T number{};
stream >> number;
if (stream.bad())
throw std::system_error(errno, std::generic_category(), throw std::system_error(errno, std::generic_category(),
"Failed to parse number from setting [" + std::string(category) + "] " + std::string(setting) "Failed to parse number from setting [" + std::string(category) + "] " + std::string(setting)
+ " value \"" + value + "\""); + " value \"" + value + "\"");
return number;
} }
return floatValue;
}
template <>
double parseNumberFromSetting<double>(
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(); CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap();
@ -139,27 +180,27 @@ namespace Settings
float Manager::getFloat(std::string_view setting, std::string_view category) float Manager::getFloat(std::string_view setting, std::string_view category)
{ {
return parseFloatingPointNumber<float>(getString(setting, category), setting, category); return parseNumberFromSetting<float>(getString(setting, category), setting, category);
} }
double Manager::getDouble(std::string_view setting, std::string_view category) double Manager::getDouble(std::string_view setting, std::string_view category)
{ {
return parseFloatingPointNumber<double>(getString(setting, category), setting, category); return parseNumberFromSetting<double>(getString(setting, category), setting, category);
} }
int Manager::getInt(std::string_view setting, std::string_view category) int Manager::getInt(std::string_view setting, std::string_view category)
{ {
return parseIntegralNumber<int>(getString(setting, category), setting, category); return parseNumberFromSetting<int>(getString(setting, category), setting, category);
} }
std::uint64_t Manager::getUInt64(std::string_view setting, std::string_view category) std::uint64_t Manager::getUInt64(std::string_view setting, std::string_view category)
{ {
return parseIntegralNumber<std::uint64_t>(getString(setting, category), setting, category); return parseNumberFromSetting<uint64_t>(getString(setting, category), setting, category);
} }
std::size_t Manager::getSize(std::string_view setting, std::string_view category) std::size_t Manager::getSize(std::string_view setting, std::string_view category)
{ {
return parseIntegralNumber<std::size_t>(getString(setting, category), setting, category); return parseNumberFromSetting<size_t>(getString(setting, category), setting, category);
} }
bool Manager::getBool(std::string_view setting, std::string_view 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) osg::Vec2f Manager::getVector2(std::string_view setting, std::string_view category)
{ {
const std::string& value = getString(setting, category); const std::string& value = getString(setting, category);
std::stringstream stream(value);
float x, y; std::vector<std::string> components;
stream >> x >> y; Misc::StringUtils::split(value, components);
if (stream.fail())
if (components.size() == 2)
{
auto x = Misc::StringUtils::toNumeric<float>(components[0]);
auto y = Misc::StringUtils::toNumeric<float>(components[1]);
if (x && y)
{
return { x.value(), y.value() };
}
}
throw std::runtime_error(std::string("Can't parse 2d vector: " + value)); throw std::runtime_error(std::string("Can't parse 2d vector: " + value));
return { x, y };
} }
osg::Vec3f Manager::getVector3(std::string_view setting, std::string_view category) osg::Vec3f Manager::getVector3(std::string_view setting, std::string_view category)
{ {
const std::string& value = getString(setting, category); const std::string& value = getString(setting, category);
std::stringstream stream(value);
float x, y, z; std::vector<std::string> components;
stream >> x >> y >> z; Misc::StringUtils::split(value, components);
if (stream.fail())
if (components.size() == 3)
{
auto x = Misc::StringUtils::toNumeric<float>(components[0]);
auto y = Misc::StringUtils::toNumeric<float>(components[1]);
auto z = Misc::StringUtils::toNumeric<float>(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)); throw std::runtime_error(std::string("Can't parse 3d vector: " + value));
return { x, y, z };
} }
void Manager::setString(std::string_view setting, std::string_view category, const std::string& value) void Manager::setString(std::string_view setting, std::string_view category, const std::string& value)

@ -5,6 +5,7 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/files/conversion.hpp> #include <components/files/conversion.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/misc/strings/format.hpp> #include <components/misc/strings/format.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <filesystem> #include <filesystem>
@ -90,7 +91,7 @@ namespace Shader
size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length(); size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length();
size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart); size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart);
std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart); std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);
lineNumber = std::stoi(lineNumberString) - 1; lineNumber = Misc::StringUtils::toNumeric<int>(lineNumberString, 2) - 1;
} }
else else
{ {
@ -159,7 +160,7 @@ namespace Shader
size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length(); size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length();
size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart); size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart);
std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart); std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);
lineNumber = std::stoi(lineNumberString) - 1; lineNumber = Misc::StringUtils::toNumeric<int>(lineNumberString, 2) - 1;
} }
else else
{ {
@ -239,7 +240,7 @@ namespace Shader
size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length(); size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length();
size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart); size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart);
std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart); std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);
lineNumber = std::stoi(lineNumberString); lineNumber = Misc::StringUtils::toNumeric<int>(lineNumberString, 2);
} }
else else
{ {

@ -2,6 +2,8 @@
#include "numericeditbox.hpp" #include "numericeditbox.hpp"
#include <components/misc/strings/conversion.hpp>
namespace Gui namespace Gui
{ {
@ -22,27 +24,25 @@ namespace Gui
void NumericEditBox::onEditTextChange(MyGUI::EditBox* sender) void NumericEditBox::onEditTextChange(MyGUI::EditBox* sender)
{ {
std::string newCaption = sender->getCaption(); const std::string newCaption = sender->getCaption();
if (newCaption.empty()) if (newCaption.empty())
{ {
return; return;
} }
try if (const auto conversion = Misc::StringUtils::toNumeric<int>(newCaption))
{ {
mValue = std::stoi(newCaption); mValue = conversion.value();
int capped = std::clamp(mValue, mMinValue, mMaxValue);
const int capped = std::clamp(mValue, mMinValue, mMaxValue);
if (capped != mValue) if (capped != mValue)
{ {
mValue = capped; mValue = capped;
setCaption(MyGUI::utility::toString(mValue)); setCaption(MyGUI::utility::toString(mValue));
} }
} }
catch (const std::invalid_argument&) else
{
setCaption(MyGUI::utility::toString(mValue));
}
catch (const std::out_of_range&)
{ {
setCaption(MyGUI::utility::toString(mValue)); setCaption(MyGUI::utility::toString(mValue));
} }

Loading…
Cancel
Save