1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 21:35:24 +00:00

Define typed settings storage with single time initialization

To make sure loaded settings have valid values doing the check once per loading.
And to make access more efficient.
This commit is contained in:
elsid 2022-06-18 02:46:47 +02:00
parent ba1f91661f
commit 3bad40153c
No known key found for this signature in database
GPG Key ID: 4DE04C198CBA7625
9 changed files with 557 additions and 1 deletions

View File

@ -42,7 +42,14 @@ add_component_dir (l10n
)
add_component_dir (settings
settings parser
categories
parser
sanitizer
sanitizerimpl
settings
settingvalue
shadermanager
values
)
add_component_dir (bsa

View File

@ -0,0 +1,15 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_SANITIZER_H
#define OPENMW_COMPONENTS_SETTINGS_SANITIZER_H
namespace Settings
{
template <class T>
struct Sanitizer
{
virtual ~Sanitizer() = default;
virtual T apply(const T& value) const = 0;
};
}
#endif

View File

@ -0,0 +1,205 @@
#include "sanitizerimpl.hpp"
#include <osg/Vec3f>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace Settings
{
namespace
{
template <class T>
struct Max final : Sanitizer<T>
{
T mMax;
explicit Max(const T& max)
: mMax(max)
{
}
T apply(const T& value) const override { return std::max(value, mMax); }
};
template <class T>
struct MaxStrict final : Sanitizer<T>
{
static_assert(std::is_floating_point_v<T>);
T mMax;
explicit MaxStrict(const T& max)
: mMax(std::nextafter(max, std::numeric_limits<T>::max()))
{
}
T apply(const T& value) const override { return std::max(value, mMax); }
};
template <>
struct MaxStrict<osg::Vec3f> final : Sanitizer<osg::Vec3f>
{
osg::Vec3f mMax;
explicit MaxStrict(const osg::Vec3f& max)
: mMax(std::nextafter(max.x(), std::numeric_limits<float>::max()),
std::nextafter(max.y(), std::numeric_limits<float>::max()),
std::nextafter(max.z(), std::numeric_limits<float>::max()))
{
}
osg::Vec3f apply(const osg::Vec3f& value) const override
{
return osg::Vec3f(
std::max(value.x(), mMax.x()), std::max(value.y(), mMax.y()), std::max(value.z(), mMax.z()));
}
};
template <class T>
struct Clamp final : Sanitizer<T>
{
T mMin;
T mMax;
explicit Clamp(const T& min, const T& max)
: mMin(min)
, mMax(max)
{
}
T apply(const T& value) const override { return std::clamp(value, mMin, mMax); }
};
template <class T>
auto getPrev(const T& value) -> std::enable_if_t<std::is_floating_point_v<T>, T>
{
assert(value > -std::numeric_limits<T>::max());
return std::nextafter(value, -std::numeric_limits<T>::max());
}
template <class T>
struct ClampStrictMax final : Sanitizer<T>
{
T mMin;
T mMax;
explicit ClampStrictMax(const T& min, const T& max)
: mMin(min)
, mMax(getPrev(max))
{
}
T apply(const T& value) const override { return std::clamp(value, mMin, mMax); }
};
template <class T>
struct Enum final : Sanitizer<T>
{
std::vector<T> mValues;
explicit Enum(std::initializer_list<T> value)
: mValues(std::make_move_iterator(value.begin()), std::make_move_iterator(value.end()))
{
}
T apply(const T& value) const override
{
if (std::find(mValues.begin(), mValues.end(), value) == mValues.end())
{
std::ostringstream message;
message << "Invalid enum value: " << value;
throw std::runtime_error(message.str());
}
return value;
}
};
template <class T>
struct EqualOrMax final : Sanitizer<T>
{
T mEqual;
T mMax;
explicit EqualOrMax(const T& equal, const T& max)
: mEqual(equal)
, mMax(max)
{
}
T apply(const T& value) const override
{
if (value == mEqual)
return value;
return std::max(value, mMax);
}
};
}
std::unique_ptr<Sanitizer<float>> makeMaxSanitizerFloat(float max)
{
return std::make_unique<Max<float>>(max);
}
std::unique_ptr<Sanitizer<int>> makeMaxSanitizerInt(int max)
{
return std::make_unique<Max<int>>(max);
}
std::unique_ptr<Sanitizer<std::size_t>> makeMaxSanitizerSize(std::size_t max)
{
return std::make_unique<Max<std::size_t>>(max);
}
std::unique_ptr<Sanitizer<std::uint64_t>> makeMaxSanitizerUInt64(std::uint64_t max)
{
return std::make_unique<Max<std::uint64_t>>(max);
}
std::unique_ptr<Sanitizer<float>> makeMaxStrictSanitizerFloat(float max)
{
return std::make_unique<MaxStrict<float>>(max);
}
std::unique_ptr<Sanitizer<osg::Vec3f>> makeMaxStrictSanitizerVec3f(const osg::Vec3f& max)
{
return std::make_unique<MaxStrict<osg::Vec3f>>(max);
}
std::unique_ptr<Sanitizer<float>> makeClampSanitizerFloat(float min, float max)
{
return std::make_unique<Clamp<float>>(min, max);
}
std::unique_ptr<Sanitizer<int>> makeClampSanitizerInt(int min, int max)
{
return std::make_unique<Clamp<int>>(min, max);
}
std::unique_ptr<Sanitizer<float>> makeClampStrictMaxSanitizerFloat(float min, float max)
{
return std::make_unique<ClampStrictMax<float>>(min, max);
}
std::unique_ptr<Sanitizer<int>> makeEnumSanitizerInt(std::initializer_list<int> values)
{
return std::make_unique<Enum<int>>(values);
}
std::unique_ptr<Sanitizer<std::string>> makeEnumSanitizerString(std::initializer_list<std::string> values)
{
return std::make_unique<Enum<std::string>>(values);
}
std::unique_ptr<Sanitizer<float>> makeEqualOrMaxSanitizerFloat(float equal, float max)
{
return std::make_unique<EqualOrMax<float>>(equal, max);
}
}

View File

@ -0,0 +1,40 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_SANITIZERIMPL_H
#define OPENMW_COMPONENTS_SETTINGS_SANITIZERIMPL_H
#include "sanitizer.hpp"
#include <osg/Vec3f>
#include <initializer_list>
#include <memory>
#include <string>
#include <string_view>
namespace Settings
{
std::unique_ptr<Sanitizer<float>> makeMaxSanitizerFloat(float max);
std::unique_ptr<Sanitizer<int>> makeMaxSanitizerInt(int max);
std::unique_ptr<Sanitizer<std::size_t>> makeMaxSanitizerSize(std::size_t max);
std::unique_ptr<Sanitizer<std::uint64_t>> makeMaxSanitizerUInt64(std::uint64_t max);
std::unique_ptr<Sanitizer<float>> makeMaxStrictSanitizerFloat(float max);
std::unique_ptr<Sanitizer<osg::Vec3f>> makeMaxStrictSanitizerVec3f(const osg::Vec3f& max);
std::unique_ptr<Sanitizer<float>> makeClampSanitizerFloat(float min, float max);
std::unique_ptr<Sanitizer<int>> makeClampSanitizerInt(int min, int max);
std::unique_ptr<Sanitizer<float>> makeClampStrictMaxSanitizerFloat(float min, float max);
std::unique_ptr<Sanitizer<int>> makeEnumSanitizerInt(std::initializer_list<int> values);
std::unique_ptr<Sanitizer<std::string>> makeEnumSanitizerString(std::initializer_list<std::string> values);
std::unique_ptr<Sanitizer<float>> makeEqualOrMaxSanitizerFloat(float equal, float max);
}
#endif

View File

@ -1,5 +1,6 @@
#include "settings.hpp"
#include "parser.hpp"
#include "values.hpp"
#include <charconv>
#include <filesystem>
@ -79,11 +80,19 @@ namespace Settings
return doubleValue;
}
#endif
template <class T>
std::string serialize(const T& value)
{
std::ostringstream stream;
stream << value;
return stream.str();
}
}
CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap();
CategorySettingValueMap Manager::mUserSettings = CategorySettingValueMap();
CategorySettingVector Manager::mChangedSettings = CategorySettingVector();
std::set<std::pair<std::string_view, std::string_view>> Manager::sInitialized;
void Manager::clear()
{
@ -136,6 +145,12 @@ namespace Settings
if (std::filesystem::exists(settingspath))
parser.loadSettingsFile(settingspath, mUserSettings, false, false);
Settings::Values::init();
for (const auto& [key, value] : mDefaultSettings)
if (!sInitialized.contains(key))
throw std::runtime_error("Default setting [" + key.first + "] " + key.second + " is not initialized");
return settingspath;
}
@ -203,6 +218,21 @@ namespace Settings
return parseNumberFromSetting<size_t>(getString(setting, category), setting, category);
}
unsigned Manager::getUnsigned(std::string_view setting, std::string_view category)
{
return parseNumberFromSetting<unsigned>(getString(setting, category), setting, category);
}
unsigned long Manager::getUnsignedLong(std::string_view setting, std::string_view category)
{
return parseNumberFromSetting<unsigned long>(getString(setting, category), setting, category);
}
unsigned long long Manager::getUnsignedLongLong(std::string_view setting, std::string_view category)
{
return parseNumberFromSetting<unsigned long long>(getString(setting, category), setting, category);
}
bool Manager::getBool(std::string_view setting, std::string_view category)
{
const std::string& string = getString(setting, category);
@ -360,4 +390,59 @@ namespace Settings
}
}
void Manager::set(std::string_view setting, std::string_view category, int value)
{
setInt(setting, category, value);
}
void Manager::set(std::string_view setting, std::string_view category, unsigned value)
{
setString(setting, category, serialize(value));
}
void Manager::set(std::string_view setting, std::string_view category, unsigned long value)
{
setString(setting, category, serialize(value));
}
void Manager::set(std::string_view setting, std::string_view category, unsigned long long value)
{
setString(setting, category, serialize(value));
}
void Manager::set(std::string_view setting, std::string_view category, float value)
{
setFloat(setting, category, value);
}
void Manager::set(std::string_view setting, std::string_view category, double value)
{
setDouble(setting, category, value);
}
void Manager::set(std::string_view setting, std::string_view category, const std::string& value)
{
setString(setting, category, value);
}
void Manager::set(std::string_view setting, std::string_view category, bool value)
{
setBool(setting, category, value);
}
void Manager::set(std::string_view setting, std::string_view category, const osg::Vec2f& value)
{
setVector2(setting, category, value);
}
void Manager::set(std::string_view setting, std::string_view category, const osg::Vec3f& value)
{
setVector3(setting, category, value);
}
void Manager::recordInit(std::string_view setting, std::string_view category)
{
sInitialized.emplace(category, setting);
}
}

View File

@ -3,6 +3,7 @@
#include "categories.hpp"
#include <set>
#include <string>
#include <string_view>
#include <vector>
@ -61,6 +62,9 @@ namespace Settings
static int getInt(std::string_view setting, std::string_view category);
static std::uint64_t getUInt64(std::string_view setting, std::string_view category);
static std::size_t getSize(std::string_view setting, std::string_view category);
static unsigned getUnsigned(std::string_view setting, std::string_view category);
static unsigned long getUnsignedLong(std::string_view setting, std::string_view category);
static unsigned long long getUnsignedLongLong(std::string_view setting, std::string_view category);
static float getFloat(std::string_view setting, std::string_view category);
static double getDouble(std::string_view setting, std::string_view category);
static const std::string& getString(std::string_view setting, std::string_view category);
@ -69,6 +73,13 @@ namespace Settings
static osg::Vec2f getVector2(std::string_view setting, std::string_view category);
static osg::Vec3f getVector3(std::string_view setting, std::string_view category);
template <class T>
static T get(std::string_view setting, std::string_view category)
{
recordInit(setting, category);
return getImpl<T>(setting, category);
}
static void setInt(std::string_view setting, std::string_view category, int value);
static void setUInt64(std::string_view setting, std::string_view category, std::uint64_t value);
static void setFloat(std::string_view setting, std::string_view category, float value);
@ -79,8 +90,86 @@ namespace Settings
static void setBool(std::string_view setting, std::string_view category, bool value);
static void setVector2(std::string_view setting, std::string_view category, osg::Vec2f value);
static void setVector3(std::string_view setting, std::string_view category, osg::Vec3f value);
static void set(std::string_view setting, std::string_view category, int value);
static void set(std::string_view setting, std::string_view category, unsigned value);
static void set(std::string_view setting, std::string_view category, unsigned long value);
static void set(std::string_view setting, std::string_view category, unsigned long long value);
static void set(std::string_view setting, std::string_view category, float value);
static void set(std::string_view setting, std::string_view category, double value);
static void set(std::string_view setting, std::string_view category, const std::string& value);
static void set(std::string_view setting, std::string_view category, bool value);
static void set(std::string_view setting, std::string_view category, const osg::Vec2f& value);
static void set(std::string_view setting, std::string_view category, const osg::Vec3f& value);
private:
static std::set<std::pair<std::string_view, std::string_view>> sInitialized;
template <class T>
static T getImpl(std::string_view setting, std::string_view category);
static void recordInit(std::string_view setting, std::string_view category);
};
template <>
inline int Manager::getImpl<int>(std::string_view setting, std::string_view category)
{
return getInt(setting, category);
}
template <>
inline unsigned Manager::getImpl<unsigned>(std::string_view setting, std::string_view category)
{
return getUnsigned(setting, category);
}
template <>
inline unsigned long Manager::getImpl<unsigned long>(std::string_view setting, std::string_view category)
{
return getUnsignedLong(setting, category);
}
template <>
inline unsigned long long Manager::getImpl<unsigned long long>(std::string_view setting, std::string_view category)
{
return getUnsignedLongLong(setting, category);
}
template <>
inline float Manager::getImpl<float>(std::string_view setting, std::string_view category)
{
return getFloat(setting, category);
}
template <>
inline double Manager::getImpl<double>(std::string_view setting, std::string_view category)
{
return getDouble(setting, category);
}
template <>
inline std::string Manager::getImpl<std::string>(std::string_view setting, std::string_view category)
{
return getString(setting, category);
}
template <>
inline bool Manager::getImpl<bool>(std::string_view setting, std::string_view category)
{
return getBool(setting, category);
}
template <>
inline osg::Vec2f Manager::getImpl<osg::Vec2f>(std::string_view setting, std::string_view category)
{
return getVector2(setting, category);
}
template <>
inline osg::Vec3f Manager::getImpl<osg::Vec3f>(std::string_view setting, std::string_view category)
{
return getVector3(setting, category);
}
}
#endif // COMPONENTS_SETTINGS_H

View File

@ -0,0 +1,70 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_SETTINGVALUE_H
#define OPENMW_COMPONENTS_SETTINGS_SETTINGVALUE_H
#include "sanitizer.hpp"
#include "settings.hpp"
#include "components/debug/debuglog.hpp"
#include <osg/io_utils>
#include <memory>
#include <stdexcept>
#include <string_view>
namespace Settings
{
template <class T>
class SettingValue
{
public:
explicit SettingValue(
std::string_view category, std::string_view name, std::unique_ptr<const Sanitizer<T>>&& sanitizer = nullptr)
: mCategory(category)
, mName(name)
, mSanitizer(std::move(sanitizer))
, mValue(sanitize(Settings::Manager::get<T>(name, category)))
{
}
const T& get() const { return mValue; }
operator const T&() const { return mValue; }
void set(const T& value)
{
if (mValue == value)
return;
mValue = sanitize(value);
Settings::Manager::set(mName, mCategory, mValue);
}
private:
const std::string_view mCategory;
const std::string_view mName;
const std::unique_ptr<const Sanitizer<T>> mSanitizer;
T mValue{};
T sanitize(const T& value) const
{
if (mSanitizer == nullptr)
return value;
try
{
T sanitizedValue = mSanitizer->apply(value);
if (sanitizedValue != value)
Log(Debug::Warning) << "Setting [" << mCategory << "] " << mName
<< " value is out of allowed values set: " << value << ", sanitized to "
<< sanitizedValue;
return sanitizedValue;
}
catch (const std::exception& e)
{
throw std::runtime_error("Invalid setting [" + std::string(mCategory) + "] " + std::string(mName)
+ " value: " + std::string(e.what()));
}
}
};
}
#endif

View File

@ -0,0 +1,12 @@
#include "values.hpp"
namespace Settings
{
Values* Values::sValues = nullptr;
void Values::init()
{
static Values values;
Values::sValues = &values;
}
}

View File

@ -0,0 +1,33 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_VALUES_H
#define OPENMW_COMPONENTS_SETTINGS_VALUES_H
#include "sanitizerimpl.hpp"
#include "settingvalue.hpp"
namespace Settings
{
class Values
{
public:
static void init();
private:
static Values* sValues;
friend const Values& values();
friend Values& valuesMutable();
};
inline const Values& values()
{
return *Values::sValues;
}
inline Values& valuesMutable()
{
return *Values::sValues;
}
}
#endif