Compare commits
No commits in common. "742e608e0103d3813a1e2c78ab970cbc5ba13489" and "c45bd6137c11a1886965d278e52f7b3de723ae1e" have entirely different histories.
742e608e01
...
c45bd6137c
@ -1,6 +1,5 @@
|
|||||||
CXX = /usr/bin/g++
|
CXX = /usr/bin/g++
|
||||||
CXXFLAGS = -std=c++20 -MMD -MP -O3
|
CXXFLAGS = -std=c++20 -MMD -MP -O3
|
||||||
CXXFLAGS += -Wall -Wpedantic -Wextra
|
|
||||||
SRCDIRS = $(wildcard */)
|
SRCDIRS = $(wildcard */)
|
||||||
IFLAGS = $(foreach dir,$(SRCDIRS), -I$(dir))
|
IFLAGS = $(foreach dir,$(SRCDIRS), -I$(dir))
|
||||||
IFLAGS += $(foreach dir,$(SRCDIRS), -I../$(dir))
|
IFLAGS += $(foreach dir,$(SRCDIRS), -I../$(dir))
|
||||||
@ -18,7 +17,7 @@ DEPENDS = ${OBJECTS:.o=.d}
|
|||||||
|
|
||||||
CXXFLAGS += $(IFLAGS)
|
CXXFLAGS += $(IFLAGS)
|
||||||
|
|
||||||
.PHONY: install uninstall update debug run clean docs test
|
.PHONY: install debug run clean docs test
|
||||||
#
|
#
|
||||||
# BUILDING
|
# BUILDING
|
||||||
#
|
#
|
||||||
@ -44,7 +43,6 @@ $(OBJECT_DIRS):
|
|||||||
#
|
#
|
||||||
# INSTALLATION
|
# INSTALLATION
|
||||||
#
|
#
|
||||||
update: uninstall install
|
|
||||||
install: $(LIB) $(HEADER_INST)
|
install: $(LIB) $(HEADER_INST)
|
||||||
@{ [ -z "$(DESTDIR)" ] && echo "Please set the DESTDIR variable (probably to /usr or /usr/local)" && exit 1; } || true
|
@{ [ -z "$(DESTDIR)" ] && echo "Please set the DESTDIR variable (probably to /usr or /usr/local)" && exit 1; } || true
|
||||||
install -D -m 755 $< $(DESTDIR)/lib/$(subst ../,,$<)
|
install -D -m 755 $< $(DESTDIR)/lib/$(subst ../,,$<)
|
||||||
|
@ -315,7 +315,7 @@ class Log {
|
|||||||
void vlog(const char* appendChars, T&& t, Args&&... args) requires (!util::Stringy<T>);
|
void vlog(const char* appendChars, T&& t, Args&&... args) requires (!util::Stringy<T>);
|
||||||
|
|
||||||
/// End for the recursion
|
/// End for the recursion
|
||||||
void vlog([[maybe_unused]] const char* appendChars) {};
|
void vlog(const char* appendChars) {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "file_io.hpp"
|
#include "file_io.hpp"
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
#include "string/conversion.hpp"
|
||||||
|
#include "string/utility.hpp"
|
||||||
|
#include "concepts.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -9,12 +13,65 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
#include <unordered_map>
|
||||||
#include "settings_manager/common.hpp"
|
#include <variant>
|
||||||
#include "settings_manager/restricted_value.hpp"
|
|
||||||
|
|
||||||
namespace gz {
|
namespace gz {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept Number = std::integral<T> || std::floating_point<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept NotNumber = !Number<T>;
|
||||||
|
|
||||||
|
/* template<typename T, typename... PackTypes> */
|
||||||
|
/* concept IntegralInPack = std::integral<T> && util::IsInPack<T, PackTypes...>; */
|
||||||
|
|
||||||
|
/* template<typename T, typename... PackTypes> */
|
||||||
|
/* concept FloatingPointInPack = std::floating_point<T> && util::IsInPack<T, PackTypes...>; */
|
||||||
|
|
||||||
|
template<typename T, typename... PackTypes>
|
||||||
|
concept NumberInPack = Number<T> && util::IsInPack<T, PackTypes...>;
|
||||||
|
|
||||||
|
template<typename T, typename... PackTypes>
|
||||||
|
concept NotNumberInPack = NotNumber<T> && util::IsInPack<T, PackTypes...>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum SettingsManagerAllowedValueTypes {
|
||||||
|
SM_RANGE, SM_LIST,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Information about the allowed values
|
||||||
|
* @details
|
||||||
|
* If type is
|
||||||
|
* - SM_RANGE -> allowedValues must contain two numbers [min, max]
|
||||||
|
* - SM_LIST -> allowedValues must contain strings, which are the allowed values
|
||||||
|
*/
|
||||||
|
template<StringConvertible... CacheTypes>
|
||||||
|
struct SettingsManagerAllowedValues {
|
||||||
|
SettingsManagerAllowedValueTypes type;
|
||||||
|
std::variant<std::vector<std::string>, std::vector<CacheTypes>... > allowedValues;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @brief Check if struct is valid.
|
||||||
|
* @throws InvalidType if:
|
||||||
|
* - the type contained in the variant allowedValues is not T
|
||||||
|
* - type is SM_RANGE but T is not an integral or floating point type
|
||||||
|
*
|
||||||
|
* @throws InvalidArgument if:
|
||||||
|
* - vector contained in allowedValues is empty
|
||||||
|
* - type is SM_RANGE but allowedValues is not of size 2
|
||||||
|
* - type is SM_RANGE but allowedValues[0] >= allowedValues[1]
|
||||||
|
*/
|
||||||
|
template<Number T, StringConvertible... CacheTypes>
|
||||||
|
void hasCorrectFormat(SettingsManagerAllowedValues<CacheTypes...>& ab);
|
||||||
|
template<NotNumber T, StringConvertible... CacheTypes>
|
||||||
|
void hasCorrectFormat(SettingsManagerAllowedValues<CacheTypes...>& av);
|
||||||
|
/* template<std::integral T, StringConvertible... CacheTypes> */
|
||||||
|
/* void hasCorrectFormat(SettingsManagerAllowedValues<CacheTypes...>& ab); */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creation info for SettingsManager
|
* @brief Creation info for SettingsManager
|
||||||
*/
|
*/
|
||||||
@ -45,6 +102,22 @@ namespace gz {
|
|||||||
* @brief Wether to write the values to filepath when destroying the SettingsManager
|
* @brief Wether to write the values to filepath when destroying the SettingsManager
|
||||||
*/
|
*/
|
||||||
bool writeFileOnExit = false;
|
bool writeFileOnExit = false;
|
||||||
|
/*
|
||||||
|
* @brief A map containing SettingsMangerAllowedValues to restrict values
|
||||||
|
* @details
|
||||||
|
* If allowedValues contains key, its value must be allowed by SettingsMangerAllowedValues struct.
|
||||||
|
* If it is not allowed, any operation that tries to set an invalid value will throw InvalidArgument or be ignored:
|
||||||
|
* - in constructor: always ignored
|
||||||
|
* - in SettingsManager::readFileOnCreation: always ignored
|
||||||
|
* - in SettingsManager::setAllowedValues: always ignored
|
||||||
|
* - in SettingsManager::set: depends on throwExceptionWhenNewValueNotAllowed
|
||||||
|
* @see sm_validity
|
||||||
|
*/
|
||||||
|
util::unordered_string_map<SettingsManagerAllowedValues<CacheTypes...>> allowedValues;
|
||||||
|
/**
|
||||||
|
* @brief Wether to throw an exception when trying to set an invalid value
|
||||||
|
*/
|
||||||
|
bool throwExceptionWhenNewValueNotAllowed = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
using SettingsCallbackFunction = std::function<void(const std::string&)>;
|
using SettingsCallbackFunction = std::function<void(const std::string&)>;
|
||||||
@ -56,6 +129,11 @@ namespace gz {
|
|||||||
* The SettingsManager is basically a map with extra features.
|
* The SettingsManager is basically a map with extra features.
|
||||||
* It stores key-value pairs in a map. Both key and value are strings, but other types can also be stored.
|
* It stores key-value pairs in a map. Both key and value are strings, but other types can also be stored.
|
||||||
*
|
*
|
||||||
|
* @section sm_validity Restricting values
|
||||||
|
* SettingsManager can restrict the values for a given keys to a range of numbers, or a list of possible strings.
|
||||||
|
* This can be done by providing @ref SettingsMangerAllowedValues::allowedValues "a map containing information on the allowed values" in the create info.
|
||||||
|
* The settings manager will then never return an invalid value, but it might still throw errors if there is no value set.
|
||||||
|
*
|
||||||
* @section sm_cache Storing other types
|
* @section sm_cache Storing other types
|
||||||
* While all values are internally strings, SettingsManager can also store different types.
|
* While all values are internally strings, SettingsManager can also store different types.
|
||||||
* The types must satisfy the concept StringConvertible = ConstructibleFromString and ConvertibleToString.
|
* The types must satisfy the concept StringConvertible = ConstructibleFromString and ConvertibleToString.
|
||||||
@ -215,11 +293,11 @@ namespace gz {
|
|||||||
* @ingroup sm_allowed_values
|
* @ingroup sm_allowed_values
|
||||||
* @brief Check if a value is allowed for key
|
* @brief Check if a value is allowed for key
|
||||||
*/
|
*/
|
||||||
template<util::NumberInPack<std::string, CacheTypes...> T>
|
template<NumberInPack<std::string, CacheTypes...> T>
|
||||||
bool isValueAllowed(const std::string& key, const T& value) const noexcept;
|
bool isValueAllowed(const std::string& key, const T& value) const noexcept;
|
||||||
/* template<IntegralInPack<std::string, CacheTypes...> T> */
|
/* template<IntegralInPack<std::string, CacheTypes...> T> */
|
||||||
/* bool isValueAllowed(const std::string& key, const T& value) const noexcept; */
|
/* bool isValueAllowed(const std::string& key, const T& value) const noexcept; */
|
||||||
template<util::NotNumberInPack<std::string, CacheTypes...> T>
|
template<NotNumberInPack<std::string, CacheTypes...> T>
|
||||||
bool isValueAllowed(const std::string& key, const T& value) const noexcept;
|
bool isValueAllowed(const std::string& key, const T& value) const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -239,7 +317,7 @@ namespace gz {
|
|||||||
template<util::IsInPack<CacheTypes...> T>
|
template<util::IsInPack<CacheTypes...> T>
|
||||||
void setAllowedValues(const std::string& key, std::vector<T>& allowedValues, SettingsManagerAllowedValueTypes type=SM_LIST);
|
void setAllowedValues(const std::string& key, std::vector<T>& allowedValues, SettingsManagerAllowedValueTypes type=SM_LIST);
|
||||||
template<util::IsInPack<CacheTypes...> T>
|
template<util::IsInPack<CacheTypes...> T>
|
||||||
void setAllowedValues(const std::string& key, std::vector<T>&& allowedValues, SettingsManagerAllowedValueTypes type=SM_LIST) { setAllowedValues<T>(key, allowedValues, type); }
|
void setAllowedValues(const std::string& key, std::vector<T>&& allowedValues, SettingsManagerAllowedValueTypes type=SM_LIST) { setAllowedValues<T>(key, allowedValues, type); };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup sm_allowed_values
|
* @ingroup sm_allowed_values
|
||||||
@ -274,7 +352,7 @@ namespace gz {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
template<std::same_as<void> Void>
|
template<std::same_as<void> Void>
|
||||||
void initCache() {}
|
void initCache() {};
|
||||||
std::set<std::string> cacheTypes;
|
std::set<std::string> cacheTypes;
|
||||||
util::unordered_string_map<util::unordered_string_map<std::variant<std::monostate, CacheTypes...>>> settingsCache;
|
util::unordered_string_map<util::unordered_string_map<std::variant<std::monostate, CacheTypes...>>> settingsCache;
|
||||||
/* template<StringConvertible T> */
|
/* template<StringConvertible T> */
|
||||||
@ -282,6 +360,23 @@ namespace gz {
|
|||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* @name Restricting values
|
||||||
|
* @see sm_validity
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
// Allowed Values
|
||||||
|
/**
|
||||||
|
* @brief Make sure the allowedValues map is valid
|
||||||
|
* @throws InvalidArgument if any of the initial allowedValues structs is invalid
|
||||||
|
*/
|
||||||
|
void initAllowedValues();
|
||||||
|
util::unordered_string_map<SettingsManagerAllowedValues<CacheTypes...>> allowedValues;
|
||||||
|
bool throwExceptionWhenNewValueNotAllowed;
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
util::unordered_string_map<std::string> settings;
|
util::unordered_string_map<std::string> settings;
|
||||||
util::unordered_string_map<SettingsCallbackFunction> settingsCallbackFunctions;
|
util::unordered_string_map<SettingsCallbackFunction> settingsCallbackFunctions;
|
||||||
|
|
||||||
@ -296,6 +391,94 @@ namespace gz {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace gz {
|
namespace gz {
|
||||||
|
/* template<StringConvertible... CacheTypes, NumberInPack<std::string, CacheTypes...> T> */
|
||||||
|
/* template<std::integral T, StringConvertible... CacheTypes> */
|
||||||
|
/* void hasCorrectFormat(SettingsManagerAllowedValues<CacheTypes...>& av) { */
|
||||||
|
/* const std::vector<T>* v = nullptr; */
|
||||||
|
/* try { */
|
||||||
|
/* v = &std::get<std::vector<T>>(av.allowedValues); */
|
||||||
|
/* } */
|
||||||
|
/* catch (std::bad_variant_access& e) { */
|
||||||
|
/* throw InvalidType("allowedValues variant does not contain type T", "SettingsManagerAllowedValues::hasCorrectFormat"); */
|
||||||
|
/* } */
|
||||||
|
/* switch (av.type) { */
|
||||||
|
/* case SM_LIST: */
|
||||||
|
/* if (v->empty()) { */
|
||||||
|
/* throw InvalidArgument("Allowed value vector needs to have at least one element when AllowedValueType is SM_LIST, but is empty.", "SettingsManagerAllowedValues::hasCorrectFormat"); */
|
||||||
|
/* } */
|
||||||
|
/* break; */
|
||||||
|
/* case SM_RANGE: */
|
||||||
|
/* static_assert(std::floating_point<T> || std::integral<T>, "Type must be integral or floating point when using SM_RANGE."); */
|
||||||
|
/* std::size_t size = std::get<std::vector<int>>(av.allowedValues).size(); */
|
||||||
|
/* if (size == 2) { */
|
||||||
|
/* v->push_back(static_cast<T>(1)); */
|
||||||
|
/* } */
|
||||||
|
/* else if (size != 3) { */
|
||||||
|
/* throw InvalidArgument("AllowedValueType is SM_RANGE with integral type but allowedValues does not have size 2 or 3.", "SettingsManagerAllowedValues::hasCorrectFormat"); */
|
||||||
|
/* } */
|
||||||
|
/* // check min < max */
|
||||||
|
/* if (v[0] >= v[1]) { */
|
||||||
|
/* throw InvalidArgument("AllowedValueType is SM_RANGE but allowedValues[0] is larger than allowedValues[1].", "SettingsManagerAllowedValues::hasCorrectFormat"); */
|
||||||
|
/* } */
|
||||||
|
/* break; */
|
||||||
|
/* } // switch */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
template<Number T, StringConvertible... CacheTypes>
|
||||||
|
void hasCorrectFormat(SettingsManagerAllowedValues<CacheTypes...>& av) {
|
||||||
|
static_assert(util::IsInPack<T, CacheTypes...>, "T must be be in pack CacheTypes");
|
||||||
|
const std::vector<T>* v = nullptr;
|
||||||
|
try {
|
||||||
|
v = &std::get<std::vector<T>>(av.allowedValues);
|
||||||
|
}
|
||||||
|
catch (std::bad_variant_access& e) {
|
||||||
|
throw InvalidType("allowedValues variant does not contain type T", "SettingsManagerAllowedValues::hasCorrectFormat");
|
||||||
|
}
|
||||||
|
switch (av.type) {
|
||||||
|
case SM_LIST:
|
||||||
|
if (v->empty()) {
|
||||||
|
throw InvalidArgument("Allowed value vector needs to have at least one element when AllowedValueType is SM_LIST, but is empty.", "SettingsManagerAllowedValues::hasCorrectFormat");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SM_RANGE:
|
||||||
|
static_assert(std::floating_point<T> || std::integral<T>, "Type must be integral or floating point when using SM_RANGE.");
|
||||||
|
std::size_t size = v->size();
|
||||||
|
if (size != 2) {
|
||||||
|
throw InvalidArgument("AllowedValueType is SM_RANGE with floating point type but allowedValues does not have size 2.", "SettingsManagerAllowedValues::hasCorrectFormat");
|
||||||
|
}
|
||||||
|
// check min <= max
|
||||||
|
if (v->at(0) > v->at(1)) {
|
||||||
|
/* throw InvalidArgument("AllowedValueType is SM_RANGE but allowedValues[0] >= allowedValues[1].", "SettingsManagerAllowedValues::hasCorrectFormat"); */
|
||||||
|
throw InvalidArgument("AllowedValueType is SM_RANGE but allowedValues[0]=" + toString(v->at(0)) + " > " + toString(v->at(1)) + "=allowedValues[1].", "SettingsManagerAllowedValues::hasCorrectFormat");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
|
}
|
||||||
|
|
||||||
|
/* template<StringConvertible... CacheTypes, NotNumberInPack<std::string, CacheTypes...> T> */
|
||||||
|
template<NotNumber T, StringConvertible... CacheTypes>
|
||||||
|
void hasCorrectFormat(SettingsManagerAllowedValues<CacheTypes...>& av) {
|
||||||
|
static_assert(util::IsInPack<T, CacheTypes...>, "T must be be in pack CacheTypes");
|
||||||
|
const std::vector<T>* v = nullptr;
|
||||||
|
try {
|
||||||
|
v = &std::get<std::vector<T>>(av.allowedValues);
|
||||||
|
}
|
||||||
|
catch (std::bad_variant_access& e) {
|
||||||
|
throw InvalidType("allowedValues variant does not contain type T", "SettingsManagerAllowedValues::hasCorrectFormat");
|
||||||
|
}
|
||||||
|
switch (av.type) {
|
||||||
|
case SM_LIST:
|
||||||
|
if (v->empty()) {
|
||||||
|
throw InvalidArgument("Allowed value vector needs to have at least one element when AllowedValueType is SM_LIST, but is empty.", "SettingsManagerAllowedValues::hasCorrectFormat");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SM_RANGE:
|
||||||
|
throw InvalidArgument("Type must be integral or floating point when using SM_RANGE, but is <" + std::string(typeid(T).name()) + ">");
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// SETTINGS MANAGER
|
// SETTINGS MANAGER
|
||||||
//
|
//
|
||||||
@ -303,6 +486,9 @@ namespace gz {
|
|||||||
SettingsManager<CacheTypes...>::SettingsManager(SettingsManagerCreateInfo<CacheTypes...>& createInfo) {
|
SettingsManager<CacheTypes...>::SettingsManager(SettingsManagerCreateInfo<CacheTypes...>& createInfo) {
|
||||||
insertFallbacks = createInfo.insertFallbacks;
|
insertFallbacks = createInfo.insertFallbacks;
|
||||||
writeFileOnExit = createInfo.writeFileOnExit;
|
writeFileOnExit = createInfo.writeFileOnExit;
|
||||||
|
throwExceptionWhenNewValueNotAllowed = createInfo.throwExceptionWhenNewValueNotAllowed;
|
||||||
|
|
||||||
|
allowedValues = std::move(createInfo.allowedValues);
|
||||||
settings = std::move(createInfo.initialValues);
|
settings = std::move(createInfo.initialValues);
|
||||||
|
|
||||||
filepath = createInfo.filepath;
|
filepath = createInfo.filepath;
|
||||||
@ -310,6 +496,13 @@ namespace gz {
|
|||||||
readFromFile(false);
|
readFromFile(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// erase invalid initalValues
|
||||||
|
for (auto it = settings.begin(); it != settings.end(); it++) {
|
||||||
|
if (!isValueAllowed<std::string>(it->first, it->second)) {
|
||||||
|
it = settings.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initCache<void, CacheTypes...>();
|
initCache<void, CacheTypes...>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,6 +620,15 @@ namespace gz {
|
|||||||
//
|
//
|
||||||
template<StringConvertible... CacheTypes>
|
template<StringConvertible... CacheTypes>
|
||||||
void SettingsManager<CacheTypes...>::set(const std::string& key, const std::string& value) {
|
void SettingsManager<CacheTypes...>::set(const std::string& key, const std::string& value) {
|
||||||
|
// check if new value is allowed
|
||||||
|
if (!isValueAllowed<std::string>(key, value)) {
|
||||||
|
if (throwExceptionWhenNewValueNotAllowed) {
|
||||||
|
throw InvalidArgument("Value '" + value + "' is not allowed. Key: '" + key + "'", "SettingsManager::set");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
// remove old value from cache
|
// remove old value from cache
|
||||||
for (auto& [type, cache] : settingsCache) {
|
for (auto& [type, cache] : settingsCache) {
|
||||||
if (cache.contains(key)) {
|
if (cache.contains(key)) {
|
||||||
@ -456,13 +658,21 @@ namespace gz {
|
|||||||
try {
|
try {
|
||||||
s = gz::toString(value);
|
s = gz::toString(value);
|
||||||
}
|
}
|
||||||
// TODO why cathes?
|
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
throw InvalidArgument("Could not convert value to string, an exception occured: '" + std::string(e.what()) + "'. Key: '" + key + "'", "SettingsManager::set<" + std::string(typeid(T).name()) + ">");
|
throw InvalidArgument("Could not convert value to string, an exception occured: '" + std::string(e.what()) + "'. Key: '" + key + "'", "SettingsManager::set<" + std::string(typeid(T).name()) + ">");
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
throw InvalidArgument("Could not convert value to string, an exception occured. Key: '" + key + "'", "SettingsManager::set<" + std::string(typeid(T).name()) + ">");
|
throw InvalidArgument("Could not convert value to string, an exception occured. Key: '" + key + "'", "SettingsManager::set<" + std::string(typeid(T).name()) + ">");
|
||||||
}
|
}
|
||||||
|
// check if new value is allowed
|
||||||
|
if (!isValueAllowed<T>(key, value)) {
|
||||||
|
if (throwExceptionWhenNewValueNotAllowed) {
|
||||||
|
throw InvalidArgument("Value '" + s + "' is not allowed. Key: '" + key + "'", "SettingsManager::set<" + std::string(typeid(T).name()) + ">");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
// remove old value from cache
|
// remove old value from cache
|
||||||
for (auto& [type, cache] : settingsCache) {
|
for (auto& [type, cache] : settingsCache) {
|
||||||
if (cache.contains(key)) {
|
if (cache.contains(key)) {
|
||||||
@ -487,6 +697,108 @@ namespace gz {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
// ALLOWED VALUES
|
||||||
|
//
|
||||||
|
template<StringConvertible... CacheTypes>
|
||||||
|
template<NumberInPack<std::string, CacheTypes...> T>
|
||||||
|
bool SettingsManager<CacheTypes...>::isValueAllowed(const std::string& key, const T& value) const noexcept {
|
||||||
|
if (!allowedValues.contains(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const std::vector<T>* av = nullptr;
|
||||||
|
try {
|
||||||
|
av = &std::get<std::vector<T>>(allowedValues.at(key).allowedValues);
|
||||||
|
}
|
||||||
|
catch (std::bad_variant_access&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (allowedValues.at(key).type) {
|
||||||
|
case SM_LIST: {
|
||||||
|
for (auto it = av->begin(); it != av->end(); it++) {
|
||||||
|
if (*it == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SM_RANGE: {
|
||||||
|
bool valid = true;
|
||||||
|
// value >= lowest
|
||||||
|
valid &= value >= av->at(0);
|
||||||
|
// value <= highest
|
||||||
|
valid &= value <= av->at(1);
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<StringConvertible... CacheTypes>
|
||||||
|
template<NotNumberInPack<std::string, CacheTypes...> T>
|
||||||
|
bool SettingsManager<CacheTypes...>::isValueAllowed(const std::string& key, const T& value) const noexcept {
|
||||||
|
if (!allowedValues.contains(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const std::vector<T>* av = nullptr;
|
||||||
|
try {
|
||||||
|
av = &std::get<std::vector<T>>(allowedValues.at(key).allowedValues);
|
||||||
|
}
|
||||||
|
catch (std::bad_variant_access&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (allowedValues.at(key).type) {
|
||||||
|
case SM_LIST: {
|
||||||
|
for (auto it = av->begin(); it != av->end(); it++) {
|
||||||
|
if (*it == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SM_RANGE: {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<StringConvertible... CacheTypes>
|
||||||
|
template<util::IsInPack<CacheTypes...> T>
|
||||||
|
void SettingsManager<CacheTypes...>::setAllowedValues(const std::string& key, std::vector<T>& allowed_vector, SettingsManagerAllowedValueTypes type) {
|
||||||
|
/* std::cout << "setAllowedValues: " << typeid(std::vector<T>).name() << " - " << typeid(T).name() << "\n"; */
|
||||||
|
SettingsManagerAllowedValues<CacheTypes...> av;
|
||||||
|
av.type = type;
|
||||||
|
av.allowedValues = std::variant<std::vector<std::string>, std::vector<CacheTypes>...>(std::move(allowed_vector));
|
||||||
|
hasCorrectFormat<T>(av);
|
||||||
|
allowedValues[key] = std::move(av);
|
||||||
|
// erase the current value if it is no longer allowed
|
||||||
|
if (settingsCache[typeid(T).name()].contains(key)) {
|
||||||
|
/* std::cout << "setAllowedValues <" << typeid(T).name() << ">\n"; */
|
||||||
|
if (!isValueAllowed<T>(key, std::get<T>(settingsCache[typeid(T).name()].at(key)))) {
|
||||||
|
settings.erase(key);
|
||||||
|
settingsCache[typeid(T).name()].erase(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (settings.contains(key)) {
|
||||||
|
try {
|
||||||
|
if (!isValueAllowed<T>(key, fromString<T>(settings.at(key)))) {
|
||||||
|
settings.erase(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
settings.erase(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<StringConvertible... CacheTypes>
|
||||||
|
void SettingsManager<CacheTypes...>::removeAllowedValues(const std::string& key) noexcept {
|
||||||
|
allowedValues.erase(key);
|
||||||
|
}
|
||||||
|
//
|
||||||
// CALLBACKS
|
// CALLBACKS
|
||||||
//
|
//
|
||||||
template<StringConvertible... CacheTypes>
|
template<StringConvertible... CacheTypes>
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "value_restriction.hpp"
|
|
||||||
|
|
||||||
namespace gz {
|
|
||||||
template<typename T>
|
|
||||||
class RestrictedValue {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Create a value that is restricted to the restriction
|
|
||||||
* @throws InvalidArgument if initial does not satisfy the restriction
|
|
||||||
*/
|
|
||||||
RestrictedValue(T&& initial, ValueRestriction<T>&& restriction);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the value.
|
|
||||||
* @details
|
|
||||||
* This function will never return a value that does not satisfy the restriction
|
|
||||||
*/
|
|
||||||
inline const T& get() const { return val; }
|
|
||||||
/**
|
|
||||||
* @brief Set a new value
|
|
||||||
* @throws InvalidArgument if initial does not satisfy the restriction
|
|
||||||
*/
|
|
||||||
void operator=(const T& v);
|
|
||||||
void operator=(T&& v);
|
|
||||||
void operator=(const std::string& s) const requires (ConstructibleFromString<T> && !std::same_as<std::string, T>);
|
|
||||||
inline const ValueRestriction<T>& getRestriction() const { return restriction; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
T val;
|
|
||||||
ValueRestriction<T> restriction;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace gz {
|
|
||||||
template<typename T>
|
|
||||||
RestrictedValue<T>::RestrictedValue(T&& initial, ValueRestriction<T>&& restriction) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void RestrictedValue<T>::operator=(T&& v) {
|
|
||||||
if (restriction.isValueAllowed(v)) {
|
|
||||||
val = std::move(v);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw InvalidArgument("Trying to assign invalid value", "RestrictedValue::operator=");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<typename T>
|
|
||||||
void RestrictedValue<T>::operator=(const T& v) {
|
|
||||||
if (restriction.isValueAllowed(v)) {
|
|
||||||
val = v;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw InvalidArgument("Trying to assign invalid value", "RestrictedValue::operator=");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<typename T>
|
|
||||||
void RestrictedValue<T>::operator=(const std::string& s) const requires (ConstructibleFromString<T> && !std::same_as<std::string, T>) {
|
|
||||||
T v;
|
|
||||||
try {
|
|
||||||
v = fromString<T>(s);
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
throw InvalidArgument(std::string("Can not convert string to type <") + typeid(T).name() + ">", "RestrictedValue::operator=");
|
|
||||||
}
|
|
||||||
*this = std::move(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace gz
|
|
@ -1,110 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common.hpp"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace gz {
|
|
||||||
enum SettingsManagerAllowedValueTypes {
|
|
||||||
SM_RANGE, SM_LIST,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A value restriction can check if a variable satisfies the restriction.
|
|
||||||
* @details
|
|
||||||
* The restriction can be:
|
|
||||||
* - SM_RANGE -> value needs to be in [min, max]
|
|
||||||
* - SM_LIST -> value must be one of
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
class ValueRestriction {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @todo allow any class that has <= and >=
|
|
||||||
* @brief Restriction is a range [lower, upper]
|
|
||||||
*/
|
|
||||||
ValueRestriction(const T& lower, const T& upper) requires util::Number<T>;
|
|
||||||
/**
|
|
||||||
* @brief Restriction is a list of allowedValues
|
|
||||||
*/
|
|
||||||
ValueRestriction(std::vector<T>&& allowedValues) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if a value is allowed according to SettingsManagerAllowedValues
|
|
||||||
*/
|
|
||||||
bool isValueAllowed(const T& value) const noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct a value from string and check if it is allowed according to SettingsManagerAllowedValues
|
|
||||||
* @throws Exceptions that occur while s is converted to T (using gz::fromString())
|
|
||||||
*/
|
|
||||||
bool isValueAllowed(const std::string& s) const requires (ConstructibleFromString<T> && !std::same_as<std::string, T>);
|
|
||||||
|
|
||||||
inline const SettingsManagerAllowedValueTypes& getType() const { return type; }
|
|
||||||
private:
|
|
||||||
const SettingsManagerAllowedValueTypes type;
|
|
||||||
std::vector<T> allowedValues; // using vector and not set to also store lower, upper in there
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace gz
|
|
||||||
|
|
||||||
namespace gz {
|
|
||||||
//
|
|
||||||
// VALUERESTICTION IMPLEMENTATION
|
|
||||||
//
|
|
||||||
template<typename T>
|
|
||||||
ValueRestriction<T>::ValueRestriction(const T& lower, const T& upper) requires util::Number<T>
|
|
||||||
: type(SettingsManagerAllowedValueTypes::SM_RANGE) {
|
|
||||||
if (lower > upper) {
|
|
||||||
throw InvalidArgument("ValueRestriction: lower(=\"" + gz::toString(lower) + "\") is larger than upper(=\"" + gz::toString(upper) + "\")");
|
|
||||||
}
|
|
||||||
allowedValues.push_back(lower);
|
|
||||||
allowedValues.push_back(upper);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
ValueRestriction<T>::ValueRestriction(std::vector<T>&& allowedValues) noexcept
|
|
||||||
: type(SettingsManagerAllowedValueTypes::SM_LIST) {
|
|
||||||
this->allowedValues = std::move(allowedValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
bool ValueRestriction<T>::isValueAllowed(const std::string& s) const requires (ConstructibleFromString<T> && !std::same_as<std::string, T>) {
|
|
||||||
T val = fromString<T>(s);
|
|
||||||
return isValue(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
bool ValueRestriction<T>::isValueAllowed(const T& value) const noexcept {
|
|
||||||
switch (this->type) {
|
|
||||||
case SM_LIST: {
|
|
||||||
for (auto it = this->allowedValues.begin(); it != this->allowedValues.end(); it++) {
|
|
||||||
if (*it == value) { return true; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SM_RANGE: {
|
|
||||||
bool valid = true;
|
|
||||||
// value >= lowest
|
|
||||||
valid &= value >= allowedValues.at(0);
|
|
||||||
// value <= highest
|
|
||||||
valid &= value <= allowedValues.at(1);
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} // switch
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // namespace gz
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef GZ_UTIL_TESTING
|
|
||||||
// TODO
|
|
||||||
TEST_CASE("")
|
|
||||||
|
|
||||||
CHECK_THROWS()
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user