This commit is contained in:
Timur A. Fatkhullin 2025-10-04 00:46:07 +03:00
parent 5fe2788cd7
commit 9c13def8be
2 changed files with 174 additions and 0 deletions

View File

@ -68,5 +68,15 @@ int main()
std::cout << "refr w: " << e.value_or(0.0) << "\n"; std::cout << "refr w: " << e.value_or(0.0) << "\n";
std::cout << "refr w: " << acfg.refractWavelength << "\n"; std::cout << "refr w: " << acfg.refractWavelength << "\n";
mcc::utils::KeyValueHolder kvh(desc);
err = kvh.setValue("C", "ewlkjfde");
if (err) {
std::cout << "cannot set value: " << err.message() << "\n";
} else {
auto vs = kvh.getValue<std::string>("C");
std::cout << "kvh[C] = " << vs.value_or("<no value>") << "\n";
}
return 0; return 0;
} }

View File

@ -3,6 +3,7 @@
#include <charconv> #include <charconv>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <expected>
#include <format> #include <format>
#include <numbers> #include <numbers>
#include <ranges> #include <ranges>
@ -399,4 +400,167 @@ static constexpr size_t FNV1aHash(std::forward_iterator auto begin, std::sentine
return hash; return hash;
} }
/* key-value pair holder */
// to follow std::variant requirements (not references, not array, not void)
template <typename T>
concept variant_valid_type_c = requires { !std::is_array_v<T> && !std::is_void_v<T> && !std::is_reference_v<T>; };
template <typename T>
concept keyvalue_record_c = requires(T t) {
requires std::same_as<decltype(t.key), std::string_view>;
requires variant_valid_type_c<decltype(t.value)>;
};
template <typename T>
concept keyvalue_desc_c = requires(T t) { []<keyvalue_record_c... Ts>(std::tuple<Ts...>) {}(t); };
template <keyvalue_desc_c DESCR_T>
class KeyValueHolder
{
public:
static constexpr std::string_view COMMENT_SEQ{"#"};
static constexpr std::string_view KEY_VALUE_DELIM{"="};
static constexpr std::string_view VALUE_ARRAY_DELIM{","};
KeyValueHolder(DESCR_T desc) : _keyValue(desc)
{
[this]<size_t... I>(std::index_sequence<I...>) {
((_hashes[I] = FNV1aHash(std::get<I>(_keyValue).key)), ...);
}(std::make_index_sequence<std::tuple_size_v<DESCR_T>>());
}
template <typename T>
std::expected<T, std::error_code> getValue(std::string_view key)
{
T v;
auto err = forKey(key, [&v]<typename VT>(const VT& val) {
if constexpr (std::convertible_to<VT, T>) {
v = val;
return std::error_code();
} else {
return std::make_error_code(std::errc::invalid_argument);
}
});
if (err) {
return std::unexpected(err);
} else {
return v;
}
}
template <typename T>
std::error_code setValue(std::string_view key, const T& value)
{
return forKey(key, [value]<typename VT>(VT& val) {
if constexpr (std::convertible_to<T, VT>) {
val = value;
return std::error_code();
} else {
return std::make_error_code(std::errc::invalid_argument);
}
});
}
template <std::ranges::contiguous_range R,
typename DeserFuncT,
std::ranges::input_range RecDelimT = std::string_view>
std::error_code fromCharRange(const R& buffer,
DeserFuncT&& deser_func,
RecDelimT rec_delim = std::string_view("\n"))
{
if constexpr (std::is_array_v<std::decay_t<R>>) { // char*, const char*
if constexpr (std::is_array_v<std::decay_t<RecDelimT>>) {
return fromCharRange(std::string_view{buffer}, std::forward<DeserFuncT>(deser_func),
std::string_view(rec_delim));
} else {
return fromCharRange(std::string_view{buffer}, std::forward<DeserFuncT>(deser_func),
std::move(rec_delim));
}
} else {
if constexpr (std::is_array_v<std::decay_t<RecDelimT>>) {
return fromCharRange(buffer, std::forward<DeserFuncT>(deser_func), std::string_view(rec_delim));
}
}
auto parse_rec = [this, &deser_func](std::string_view rec) -> std::error_code {
std::string_view key, value;
if (rec.size()) {
auto found = std::ranges::search(rec, COMMENT_SEQ);
if (found.begin() != rec.end()) { // there was the comment sequence in record
rec = rec.substr(std::distance(rec.begin(), found.begin()));
}
if (rec.size()) {
found = std::ranges::search(rec, KEY_VALUE_DELIM);
if (found.begin() != rec.begin()) { // ignore an empty key
key = trimSpaces(std::string_view(rec.begin(), found.begin()), TrimType::TRIM_RIGHT);
value = std::string_view(found.end(), rec.end());
return forKey(key, [value, &deser_func]<typename VT>(VT& v) { return deser_func(value, v); });
}
}
}
};
auto curr_buffer = std::string_view(buffer.begin(), buffer.end());
bool buffer_end = false;
std::error_code ec;
std::string_view rec;
auto delim_size = std::ranges::size(rec_delim);
if (delim_size == 0) { // just one record
return parse_rec(trimSpaces(buffer, TrimType::TRIM_LEFT));
}
do {
auto found = std::ranges::search(curr_buffer, rec_delim);
if (found.begin() == curr_buffer.end()) {
buffer_end = true;
}
rec = mcc::utils::trimSpaces(std::string_view(curr_buffer.begin(), found.begin()), TrimType::TRIM_LEFT);
curr_buffer = {found.end(), curr_buffer.end()};
ec = parse_rec(rec);
} while (!buffer_end || !ec);
return ec;
}
protected:
DESCR_T _keyValue;
std::array<size_t, std::tuple_size_v<DESCR_T>> _hashes;
std::error_code forKey(std::string_view key, auto&& func)
{
return forHash<0>(FNV1aHash(key), std::forward<decltype(func)>(func));
}
template <size_t I = 0>
std::error_code forHash(size_t hash, auto&& func)
{
if constexpr (I < std::tuple_size_v<DESCR_T>) {
if (hash == _hashes[I]) {
return std::forward<decltype(func)>(func)(std::get<I>(_keyValue).value);
} else {
return forHash<I + 1>(hash, std::forward<decltype(func)>(func));
}
}
return std::make_error_code(std::errc::argument_out_of_domain);
}
};
} // namespace mcc::utils } // namespace mcc::utils