17#ifndef TOOLBOX_UTIL_CONFIG_HPP
18#define TOOLBOX_UTIL_CONFIG_HPP
28#include <unordered_map>
32inline namespace util {
34template <
typename FnT>
37 using namespace std::literals::string_literals;
40 std::unordered_map<std::string, KeyClassification, string_hash, std::equal_to<>>
key_class_map;
43 while (std::getline(
is,
line)) {
49 if (
line.empty() ||
line[0] ==
'#') {
53 if (
line.front() ==
'[' &&
line.back() ==
']') {
55 name->assign(
line, 1,
line.size() - 2);
73 std::optional<KeyClassification>
key_class;
80 throw std::runtime_error{
81 std::string{
key}.append(
" is already set as a single-valued key (with '=') "
82 "and cannot be reassigned with '+='")};
85 throw std::runtime_error{
86 std::string{
key}.append(
" is already set as a multi-valued key (with '+=') "
87 "and cannot be reassigned with '='")};
92 : KeyClassification::SingleValued;
95 fn(std::move(
key), std::move(
val));
118 const
std::
string& get(const
std::
string& key) const;
119 const
char* get(const
std::
string& key, const
char* dfl) const;
120 const
char* get(const
std::
string& key,
std::nullptr_t)
const
122 return get(key,
static_cast<const char*
>(
nullptr));
125 template <
typename ValueT>
126 ValueT
get(
const std::string& key)
const
128 const auto it{get_last_value(key)};
129 if (it != map_.end()) {
130 return transform_value<ValueT>(it->second);
133 throw std::runtime_error{std::string{
"missing config key: "} + key};
135 return parent_->get<ValueT>(key);
137 template <
typename ValueT>
138 ValueT
get(
const std::string& key, ValueT dfl)
const
140 const auto it{get_last_value(key)};
141 if (it != map_.end()) {
142 return transform_value<ValueT>(it->second);
144 return parent_ ? parent_->get<ValueT>(key, dfl) : dfl;
149 auto [begin, end] = map_.equal_range(key);
151 auto fn = [](
const auto& kvp) ->
const std::string& {
return kvp.second; };
152 auto rng = std::ranges::subrange(begin, end, map_.count(key)) | std::views::transform(fn);
158 return parent_ ? parent_->get_multi(key) : rng;
161 template <
typename ValueT>
163 return get_multi(key) | std::views::transform(transform_value<ValueT>);
166 std::size_t
size() const noexcept {
return map_.size(); }
167 void clear() noexcept { map_.clear(); }
172 return read_section(is, &next);
176 return read_section(is, next);
178 void insert(std::string key, std::string val)
180 map_.emplace(std::move(key), std::move(val));
184 template<
typename... ValueTs>
186 void set(const
std::
string& key, ValueTs ... vals)
188 static_assert(
sizeof...(ValueTs) > 0,
"at least 1 value is required");
191 (insert(key, std::string(std::move(vals))), ...);
197 template <
typename ValueT>
198 static ValueT transform_value(
const std::string& v)
200 if constexpr (std::is_same_v<ValueT, std::string_view>) {
203 return std::string_view{v};
204 }
else if constexpr (std::is_enum_v<ValueT>) {
205 return ValueT{from_string<std::underlying_type_t<ValueT>>(v)};
207 return from_string<ValueT>(v);
211 auto get_last_value(
const std::string& key)
const
212 -> std::multimap<std::string, std::string>::const_iterator
214 auto [first, second] = map_.equal_range(key);
215 return (first == second) ? map_.end() : std::prev(second);
218 std::istream& read_section(std::istream& is, std::string* next);
219 std::multimap<std::string, std::string> map_;
220 Config* parent_{
nullptr};
236 void clear() noexcept;
237 void read(
std::istream& is);
243 auto it = map_.find(name);
244 return it != map_.end() ? it->second : root_;
248 return section(std::string{name});
251 template <
typename F>
254 f(std::declval<const MultiConfig::MapType::key_type&>(),
255 std::declval<const MultiConfig::MapType::mapped_type&>())
258 std::for_each(map_.begin(), map_.end(), [&f](
const auto& kv) {
259 f(kv.first, kv.second);
266 using MapType = std::map<std::string, Config>;