userver: userver/formats/parse/variant.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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