Added refined config struct templates

This commit is contained in:
pixl 2022-01-11 18:02:40 -05:00
parent 62095a3e37
commit fbc3a1a472
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
3 changed files with 650 additions and 0 deletions

206
src/logid/config/group.h Normal file
View File

@ -0,0 +1,206 @@
/*
* Copyright 2022 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOGID_CONFIG_GROUP_H
#define LOGID_CONFIG_GROUP_H
#include <libconfig.h++>
#include <type_traits>
#include <functional>
#include <utility>
#include "../util/log.h"
namespace logid::config {
template <typename T>
void set(libconfig::Setting& parent,
const std::string& name,
const T& t);
template <typename T>
void set(libconfig::Setting& parent, const T& t);
template <typename T>
T get(const libconfig::Setting& parent, const std::string& name);
template <typename T>
void append(libconfig::Setting& list, const T& t);
namespace {
template <typename T, typename... M>
struct group_io { };
template <typename T>
struct group_io<T> {
static void get(
[[maybe_unused]] const libconfig::Setting& s,
[[maybe_unused]] T* t,
[[maybe_unused]] const std::vector<std::string>& names,
[[maybe_unused]] const std::size_t index) { }
static void set(
[[maybe_unused]] libconfig::Setting& s,
[[maybe_unused]] const T* t,
[[maybe_unused]] const std::vector<std::string>& names,
[[maybe_unused]] const std::size_t index) { }
};
template <typename T, typename A, typename... M>
struct group_io<T, A, M...> {
static void get(
const libconfig::Setting& s, T* t,
const std::vector<std::string>& names,
const std::size_t index, A T::* arg, M T::*... rest) {
auto& x = t->*(arg);
A old {x};
try {
x = config::get<A>(s, names[index]);
group_io<T, M...>::get(s, t, names, index+1, rest...);
} catch(libconfig::SettingTypeException& e) {
x = old;
throw;
} catch(libconfig::SettingException& e) {
x = old;
throw libconfig::SettingTypeException(s);
}
}
static void set(
libconfig::Setting& s, const T* t,
const std::vector<std::string>& names,
const std::size_t index, A T::* arg, M T::*... rest) {
config::set(s, names[index], t->*(arg));
group_io<T, M...>::set(s, t, names, index+1, rest...);
}
};
}
template <typename Sign>
struct signed_group;
struct group {
private:
const std::vector<std::string> _names;
const std::function<void(const libconfig::Setting&, group*,
const std::vector<std::string>&)> _getter;
const std::function<void(libconfig::Setting&, const group*,
const std::vector<std::string>&)> _setter;
template <typename Sign>
friend struct signed_group;
protected:
template <typename T, typename... M>
group(const std::array<std::string, sizeof...(M)>& names,
M T::*... args) :
_names (names.begin(), names.end()),
_getter ([args...](const libconfig::Setting& s, group* g,
const std::vector<std::string>& names) {
T* t = dynamic_cast<T*>(g);
group_io<T, M...>::get(s, t, names, 0, args...);
}),
_setter ([args...](libconfig::Setting& s, const group* g,
const std::vector<std::string>& names) {
const T* t = dynamic_cast<const T*>(g);;
group_io<T, M...>::set(s, t, names, 0, args...);
}) {
static_assert(std::is_base_of<group, T>::value);
}
group() : _getter ([](const libconfig::Setting&, group*,
const std::vector<std::string>&) { }),
_setter ([](libconfig::Setting&, const group*,
const std::vector<std::string>&) { }) { }
public:
group(const group& o) = default;
group(group&& o) noexcept = default;
group& operator=(const group&) {
return *this;
}
group& operator=(group&&) noexcept {
return *this;
}
virtual ~group() = default;
virtual void save(libconfig::Setting& setting) const {
_setter(setting, this, _names);
}
virtual void load(const libconfig::Setting& setting) {
_getter(setting, this, _names);
}
};
namespace {
template <typename T>
struct normalize_signature {
static const T& make(const T& ret) { return ret; }
};
template <>
struct normalize_signature<std::string> {
static std::string make(const std::string& data) {
std::string ret = data;
std::transform(ret.begin(), ret.end(),
ret.begin(), ::tolower);
return ret;
}
};
}
template <typename Sign>
struct signed_group : public group {
private:
const std::string _sig_field;
const Sign _signature;
protected:
signed_group(std::string sign_name, const Sign& sign_data) :
group(), _sig_field (std::move(sign_name)),
_signature (normalize_signature<Sign>::make(sign_data)) { }
template <typename T, typename... M>
signed_group(
std::string sign_name, const Sign& sign_data,
const std::array<std::string, sizeof...(M)>& names,
M T::*... args) : group(names, args...),
_sig_field (std::move(sign_name)),
_signature (normalize_signature<Sign>::make(sign_data)) { }
public:
signed_group(const signed_group& o) = default;
signed_group(signed_group&& o) noexcept = default;
signed_group& operator=(const signed_group&) {
return *this;
}
signed_group& operator=(signed_group&&) noexcept {
return *this;
}
void save(libconfig::Setting& setting) const override {
set(setting, _signature, _sig_field);
_setter(setting, this, _names);
}
void load(const libconfig::Setting& setting) override {
if(normalize_signature<Sign>::make(get<Sign>(setting, _sig_field))
!= _signature)
throw libconfig::SettingTypeException(setting);
_getter(setting, this, _names);
}
};
}
#endif //LOGID_CONFIG_GROUP_H

46
src/logid/config/map.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright 2022 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOGID_CONFIG_MAP_H
#define LOGID_CONFIG_MAP_H
#include "group.h"
#include <map>
#include <optional>
#include <utility>
namespace logid::config {
template<size_t N>
struct string_literal {
constexpr string_literal(const char (&str)[N]) {
std::copy_n(str, N, value);
}
char value[N];
};
// Warning: map must be a variant of groups or a group
template <typename K, typename V, string_literal KeyName>
class map : public std::map<K, V> {
public:
template <typename... Args>
map(Args... args) :
std::map<K, V>(std::forward<Args>(args)...) { }
};
}
#endif //LOGID_CONFIG_MAP_H

398
src/logid/config/types.h Normal file
View File

@ -0,0 +1,398 @@
/*
* Copyright 2022 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOGID_CONFIG_PRIMITIVE_H
#define LOGID_CONFIG_PRIMITIVE_H
#include <libconfig.h++>
#include <type_traits>
#include <optional>
#include <variant>
#include "group.h"
#include "map.h"
#include "../util/log.h"
/// TODO: A single element failing should not cause the container to be invalid.
namespace logid::config {
namespace {
template <typename T>
struct config_io {
static_assert(std::is_base_of<group, T>::value);
static T get(const libconfig::Setting& parent,
const std::string& name) {
T t {};
t.load(parent.lookup(name));
return t;
}
static T get(const libconfig::Setting& setting) {
T t {};
t.load(setting);
return t;
}
static void set(libconfig::Setting& parent,
const std::string& name,
const T& t) {
if(!parent.exists(name)) {
parent.add(name, libconfig::Setting::TypeGroup);
} else if(parent.lookup(name).getType()
!= libconfig::Setting::TypeGroup) {
}
t.save(parent.lookup(name));
}
static void set(libconfig::Setting& setting, const T& t) {
t.save(setting);
}
static void append(libconfig::Setting& list, const T& t) {
auto& x = list.add(libconfig::Setting::TypeGroup);
set(x, t);
}
};
template <typename T, libconfig::Setting::Type TypeEnum>
struct primitive_io {
static T get(const libconfig::Setting& parent,
const std::string& name) {
return parent.lookup(name);
}
static T get(const libconfig::Setting& setting) {
return setting;
}
static void set(libconfig::Setting& parent,
const std::string& name,
const T& t) {
if(!parent.exists(name))
parent.add(name, TypeEnum);
set(parent.lookup(name), t);
}
static void set(libconfig::Setting& setting, const T& t) {
setting = t;
}
static void append(libconfig::Setting& list, const T& t) {
auto& x = list.add(TypeEnum);
set(x, t);
}
};
template <>
struct config_io<bool> : public primitive_io<bool,
libconfig::Setting::TypeBoolean> { };
template <>
struct config_io<int8_t> : public primitive_io<int,
libconfig::Setting::TypeInt> { };
template <>
struct config_io<uint8_t> : public primitive_io<int,
libconfig::Setting::TypeInt> { };
template <>
struct config_io<short> : public primitive_io<int,
libconfig::Setting::TypeInt> { };
template <>
struct config_io<unsigned short> : public primitive_io<int,
libconfig::Setting::TypeInt> { };
template <>
struct config_io<int> : public primitive_io<int,
libconfig::Setting::TypeInt> { };
template <>
struct config_io<unsigned int> : public primitive_io<int,
libconfig::Setting::TypeInt> { };
template <>
struct config_io<long> : public primitive_io<long,
libconfig::Setting::TypeInt64> { };
template <>
struct config_io<unsigned long> : public primitive_io<long,
libconfig::Setting::TypeInt64> { };
template <>
struct config_io<long long> : public primitive_io<long long,
libconfig::Setting::TypeInt64> { };
template <>
struct config_io<unsigned long long> : public primitive_io<long long,
libconfig::Setting::TypeInt64> { };
template <>
struct config_io<float> : public primitive_io<float,
libconfig::Setting::TypeFloat> { };
template <>
struct config_io<double> : public primitive_io<double,
libconfig::Setting::TypeFloat> { };
template <>
struct config_io<std::string> : public primitive_io<std::string,
libconfig::Setting::TypeString> { };
template <typename... T>
struct config_io<std::variant<T...>> {
private:
template <typename Singleton>
static std::variant<T...> try_each(
const libconfig::Setting& setting) {
try {
return config_io<Singleton>::get(setting);
} catch(libconfig::SettingTypeException& e) {
throw;
}
}
template <typename First, typename Next, typename... Rest>
static std::variant<T...> try_each(
const libconfig::Setting& setting) {
try {
return config_io<First>::get(setting);
} catch(libconfig::SettingException& e) {
return try_each<Next, Rest...>(setting);
}
}
public:
static std::variant<T...> get(const libconfig::Setting& setting) {
return try_each<T...>(setting);
}
static std::variant<T...> get(const libconfig::Setting& parent,
const std::string& name) {
return get(parent.lookup(name));
}
static void set(libconfig::Setting& setting,
const std::variant<T...>& t) {
std::visit([&setting](auto&& arg){
config::set(setting, arg);
}, t);
}
static void set(libconfig::Setting& parent,
const std::string& name,
const std::variant<T...>& t) {
std::visit([&parent, &name](auto&& arg){
config::set(parent, name, arg);
}, t);
}
static void append(libconfig::Setting& list,
const std::variant<T...>& t) {
std::visit([&list](auto&& arg){
config::append(list, arg);
}, t);
}
};
template <typename T>
struct config_io<std::vector<T>> {
static std::vector<T> get(const libconfig::Setting& setting) {
const auto size = setting.getLength();
std::vector<T> t(size);
for(int i = 0; i < size; ++i)
t[i] = config_io<T>::get(setting[i]);
return t;
}
static std::vector<T> get(const libconfig::Setting& parent,
const std::string& name) {
return get(parent.lookup(name));
}
static void set(libconfig::Setting& setting,
const std::vector<T>& t) {
const auto orig_size = setting.getLength();
for(int i = 0; i < orig_size; ++i)
config_io<T>::set(setting[i], t[i]);
for(int i = orig_size; i < t.size(); ++i)
config_io<T>::append(setting, t[i]);
}
static void set(libconfig::Setting& parent,
const std::string& name,
const std::vector<T>& t) {
if (!parent.exists(name)) {
parent.add(name, libconfig::Setting::TypeArray);
} else if(!parent.lookup(name).isArray()) {
parent.remove(name);
parent.add(name, libconfig::Setting::TypeArray);
}
set(parent.lookup(name), t);
}
static void append(libconfig::Setting& list,
const std::vector<T>& t) {
auto& s = list.add(libconfig::Setting::TypeArray);
set(s, t);
}
};
template <typename K, typename V, string_literal KeyName>
struct config_io<map<K, V, KeyName>> {
static map<K, V, KeyName> get(const libconfig::Setting& setting) {
const auto size = setting.getLength();
map<K, V, KeyName> t;
for(int i = 0; i < size; ++i) {
auto& s = setting[i];
t.emplace(config_io<K>::get(s.lookup(KeyName.value)),
config_io<V>::get(s));
}
return t;
}
static map<K, V, KeyName> get(const libconfig::Setting& parent,
const std::string& name) {
return get(parent.lookup(name));
}
static void set(libconfig::Setting& setting,
const map<K, V, KeyName>& t) {
while(setting.getLength() != 0)
setting.remove((int)0);
for(auto& x : t) {
auto& s = setting.add(libconfig::Setting::TypeGroup);
config_io<V>::set(s, x.second);
config_io<K>::set(s, KeyName.value, x.first);
}
}
static void set(libconfig::Setting& parent,
const std::string& name,
const map<K, V, KeyName>& t) {
if (!parent.exists(name)) {
parent.add(name, libconfig::Setting::TypeList);
} else if(!parent.lookup(name).isArray()) {
parent.remove(name);
parent.add(name, libconfig::Setting::TypeList);
}
set(parent.lookup(name), t);
}
static void append(libconfig::Setting& list,
const map<K, V, KeyName>& t) {
auto& s = list.add(libconfig::Setting::TypeList);
set(s, t);
}
};
template <typename... T>
struct config_io<std::vector<std::variant<T...>>> {
static std::vector<std::variant<T...>> get(
const libconfig::Setting& setting) {
const auto size = setting.getLength();
std::vector<std::variant<T...>> t(size);
for(int i = 0; i < size; ++i)
t[i] = config_io<std::variant<T...>>::get(setting[i]);
return t;
}
static std::vector<std::variant<T...>> get(
const libconfig::Setting& parent, const std::string& name) {
return get(parent.lookup(name));
}
static void set(libconfig::Setting& setting,
const std::vector<std::variant<T...>>& t) {
while(setting.getLength() != 0)
setting.remove((int)0);
for(int i = 0; i < t.size(); ++i)
config_io<std::variant<T...>>::append(setting, t[i]);
}
static void set(libconfig::Setting& parent,
const std::string& name,
const std::vector<std::variant<T...>>& t) {
if (!parent.exists(name)) {
parent.add(name, libconfig::Setting::TypeList);
} else if(!parent.lookup(name).isList()) {
parent.remove(name);
parent.add(name, libconfig::Setting::TypeList);
}
set(parent.lookup(name), t);
}
static void append(libconfig::Setting& list,
const std::variant<std::variant<T...>>& t) {
auto& s = list.add(libconfig::Setting::TypeList);
set(s, t);
}
};
template <typename T>
struct config_io<std::optional<T>> {
static std::optional<T> get(const libconfig::Setting& parent,
const std::string& name) {
try {
return config_io<T>::get(parent.lookup(name));
} catch(libconfig::SettingException& e) {
return {};
}
}
static void set(libconfig::Setting& parent,
const std::string& name,
const std::optional<T>& t) {
if (t.has_value())
config_io<T>::set(parent, name, t.value());
}
};
// Optionals may not appear as part of a list or array
template <typename T, typename... Rest>
struct config_io<std::variant<std::optional<T>, Rest...>> {
static_assert(!sizeof(std::optional<T>), "Invalid type");
};
template <typename T>
struct config_io<std::vector<std::optional<T>>> {
static_assert(!sizeof(std::optional<T>), "Invalid type");
};
template <typename T>
struct config_io<std::optional<std::optional<T>>> {
static_assert(!sizeof(std::optional<T>), "Invalid type");
};
}
template <typename T>
void set(libconfig::Setting& parent,
const std::string& name,
const T& t) {
config_io<T>::set(parent, name, t);
}
template <typename T>
void set(libconfig::Setting& setting, const T& t) {
config_io<T>::set(setting, t);
}
template <typename T>
void append(libconfig::Setting& list, const T& t) {
config_io<T>::set(list, t);
}
template <typename T>
T get(const libconfig::Setting& setting) {
return config_io<T>::get(setting);
}
template <typename T>
T get(const libconfig::Setting& parent, const std::string& name) {
return config_io<T>::get(parent, name);
}
}
#endif //LOGID_CONFIG_PRIMITIVE_H