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.
depth-refraction
elsid 3 years ago
parent ba1f91661f
commit 3bad40153c
No known key found for this signature in database
GPG Key ID: 4DE04C198CBA7625

@ -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

@ -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

@ -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);
}
}

@ -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

@ -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);
}
}

@ -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

@ -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

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

@ -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
Loading…
Cancel
Save