userver: userver/formats/parse/variant.hpp Source File
Loading...
Searching...
No Matches
variant.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/parse/variant.hpp
4/// @brief Ineffective but generic parser for std::variant type
5///
6/// Parsing is performed for each of the N alternatives in variant, N-1
7/// exceptions is thrown and caught during the parsing.
8///
9/// @ingroup userver_universal userver_formats_parse
10
11#include <optional>
12#include <typeinfo>
13#include <variant>
14
15#include <userver/compiler/demangle.hpp>
16#include <userver/formats/parse/to.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace formats::parse {
21
22template <typename ParseException, typename Variant, typename TypeA>
23[[noreturn]] void ThrowVariantAmbiguousParse(const std::string& path,
24 std::type_index type_b) {
25 throw ParseException(
26 "Value of '" + path +
27 "' is ambiguous, it is parseable into multiple variants of '" +
28 compiler::GetTypeName<Variant>() + "', at least '" +
29 compiler::GetTypeName<TypeA>() + "' and '" +
30 compiler::GetTypeName(type_b) + "'");
31}
32
33template <class ParseException, typename Variant>
34[[noreturn]] void ThrowVariantParseException(const std::string& path) {
35 throw ParseException("Value of '" + path + "' cannot be parsed as " +
36 compiler::GetTypeName<Variant>());
37}
38
39namespace impl {
40template <class T, class Value, typename Result>
41void ParseVariantSingle(const Value& value, std::optional<Result>& result) {
42 if (result) {
43 const auto old_type = std::visit(
44 [](const auto& v) -> std::type_index { return typeid(v); }, *result);
45 try {
46 value.template As<T>();
47 } catch (const std::exception&) {
48 return;
49 }
50
51 ThrowVariantAmbiguousParse<typename Value::ParseException, Result, T>(
52 value.GetPath(), old_type);
53 } else {
54 // No result yet
55 try {
56 result = value.template As<T>();
57 } catch (const std::exception&) {
58 }
59 }
60}
61
62} // namespace impl
63
64template <class Value, typename... Types>
65std::variant<Types...> Parse(const Value& value,
66 formats::parse::To<std::variant<Types...>>) {
67 std::optional<std::variant<Types...>> result;
68 (impl::ParseVariantSingle<Types>(value, result), ...);
69
70 if (!result) {
71 ThrowVariantParseException<typename Value::ParseException,
72 std::variant<Types...>>(value.GetPath());
73 }
74
75 return std::move(*result);
76}
77
78} // namespace formats::parse
79
80USERVER_NAMESPACE_END