alpaqa 1.0.0a17
Nonconvex constrained optimization
Loading...
Searching...
No Matches
json.tpp
Go to the documentation of this file.
5
6#include <nlohmann/json.hpp>
7#include <functional>
8#include <stdexcept>
9#include <type_traits>
10
11namespace alpaqa::params {
12
14
15namespace detail {
16
17std::string join_sorted_keys(const auto &members) {
18 auto keys = std::views::keys(members);
19 std::vector<std::string> sorted_keys{keys.begin(), keys.end()};
21 return util::join(sorted_keys, {.sep = ", ", .empty = "∅"});
22}
23
24template <class S>
25[[gnu::noinline]] auto
26find_param_or_throw(const std::string &key, const attribute_table_t<S> &members,
28 const std::string &type_name) ->
30 auto it = members.find(key);
31 // If member was not found
32 if (it == members.end()) {
33 // Perhaps it's an alias to another member?
34 auto alias_it = aliases.find(key);
35 // If it's not an alias either, raise an error
36 if (alias_it == aliases.end()) {
38 "Invalid key '" + key + "' for type '" + type_name +
39 "',\n possible keys are: " + join_sorted_keys(members) +
40 " (aliases: " + join_sorted_keys(aliases) + ")");
41 }
42 // Resolve the alias and make sure that the target exists
43 it = members.find(alias_it->second);
44 if (it == members.end())
45 throw std::logic_error("Alias '" + std::string(alias_it->first) +
46 "' refers to nonexistent option '" +
47 std::string(alias_it->second) + "' in '" +
48 type_name + "'");
49 }
50 return it;
51}
52
53template <class S>
54[[gnu::noinline]] auto
55find_param_or_throw(const std::string &key, const attribute_table_t<S> &members,
56 const std::false_type &, const std::string &type_name) ->
58 auto it = members.find(key);
59 // If member was not found
60 if (it == members.end()) {
62 "Invalid key '" + key + "' for type '" + type_name +
63 "',\n possible keys are: " + join_sorted_keys(members));
64 }
65 return it;
66}
67
68template <class Aliases>
69[[gnu::noinline]] void set_param_json(const any_ptr &t, const json &j,
70 const attribute_table_t<json> &members,
71 const Aliases &aliases,
72 const std::string &type_name) {
73 if (!j.is_object())
75 "Invalid value " + to_string(j) + " for type '" + type_name +
76 "' (expected object, but got " + j.type_name() + ')');
77 // Loop over all items in the JSON object
78 for (auto &&el : j.items()) {
79 const auto &key = el.key();
80 auto it = find_param_or_throw(key, members, aliases, type_name);
81 // Member was found, invoke its setter (and possibly recurse)
82 try {
83 it->second.set(t, el.value());
84 } catch (invalid_json_param &e) {
85 // Keep a backtrace of the JSON keys for error reporting
86 e.backtrace.push_back(key);
87 throw;
88 }
89 }
90}
91
92} // namespace detail
93
94template <>
95struct attribute_accessor<json> {
96 template <class T, class T_actual, class A>
98 return {
99 .set{[attr](const any_ptr &t, const json &s) {
100 return set_param(t.template cast<T>()->*attr, s);
101 }},
102 .get{[attr](const any_ptr &t, json &s) {
103 return get_param(t.template cast<const T>()->*attr, s);
104 }},
105 };
106 }
107 std::function<void(const any_ptr &, const json &)> set;
108 std::function<void(const any_ptr &, json &)> get;
109};
110
111template <class T>
112 requires requires { attribute_table<T, json>::table; }
113void set_param_default(T &t, const json &j) {
114 // Dictionary of members
115 const auto &members = attribute_table<T, json>::table;
116 if constexpr (requires { attribute_alias_table<T, json>::table; })
117 detail::set_param_json(&t, j, members,
119 demangled_typename(typeid(T)));
120 else
121 detail::set_param_json(&t, j, members, std::false_type{},
122 demangled_typename(typeid(T)));
123}
124
125template <class T>
126 requires requires { enum_table<T, json>::table; }
127void set_param_default(T &t, const json &j) {
128 if (!j.is_string())
129 throw invalid_json_param("Invalid value " + to_string(j) +
130 " for enum '" + demangled_typename(typeid(T)) +
131 "' (expected string, but got " +
132 j.type_name() + ')');
133 // Dictionary of members
134 const auto &m = enum_table<T, json>::table;
135 std::string value = j;
136 auto it = m.find(value);
137 if (it == m.end()) {
138 throw invalid_json_param(
139 "Invalid value '" + value + "' for enum '" +
140 demangled_typename(typeid(T)) +
141 "',\n possible values are: " + detail::join_sorted_keys(m));
142 }
143 t = it->second.value;
144}
145
146template <class T>
147 requires requires { enum_table<T, json>::table; }
148void get_param_default(const T &t, json &j) {
149 j = enum_name(t);
150}
151
152template <class T>
153 requires requires { attribute_table<T, json>::table; }
154void get_param_default(const T &t, json &s) {
155 s = json::object();
156 const auto &m = attribute_table<T, json>::table;
157 for (auto &&[k, v] : m)
158 v.get(&t, s[std::string{k}]);
159}
160
161} // namespace alpaqa::params
Like std::any, but storing just the pointer, without any dynamic allocation.
Definition structs.hpp:17
std::string demangled_typename(const std::type_info &t)
Get the pretty name of the given type as a string.
void set_param_json(const any_ptr &t, const json &j, const attribute_table_t< json > &members, const Aliases &aliases, const std::string &type_name)
Definition json.tpp:69
auto find_param_or_throw(const std::string &key, const attribute_table_t< S > &members, const attribute_alias_table_t< S > &aliases, const std::string &type_name) -> typename attribute_table_t< S >::const_iterator
Definition json.tpp:26
std::string join_sorted_keys(const auto &members)
Definition json.tpp:17
std::map< std::string_view, std::string_view > attribute_alias_table_t
Dictionary that maps struct attribute names to type-erased functions that set those attributes.
Definition structs.hpp:71
void get_param(const T &, json &j)
Get the first argument as a JSON object j.
Definition json.cpp:252
std::map< std::string_view, attribute_accessor< S > > attribute_table_t
Dictionary that maps struct attribute names to type-erased functions that set those attributes.
Definition structs.hpp:45
void get_param_default(const T &t, json &j)
Definition json.tpp:148
void set_param_default(T &t, const json &j)
Definition json.tpp:113
void set_param(T &, const json &j)
Update/overwrite the first argument based on the JSON object j.
Definition json.cpp:248
Function wrapper to set attributes of a struct, type-erasing the type of the attribute.
Definition structs.hpp:14
Specialize this type to define the alternative attribute name to attribute setters dictionaries for a...
Definition structs.hpp:76
Specialize this type to define the attribute name to attribute setters dictionaries for a struct type...
Definition structs.hpp:50
Specialize this type to define the enumerator name to value dictionaries for an enum type T.
Definition structs.hpp:103
std::string join(std::ranges::input_range auto strings, join_opt opt={})
Join the list of strings into a single string, using the separator given by opt.
void sort_case_insensitive(auto &range)
Sort the given range of strings in-place in a case-insensitive manner.
EigenConfigd DefaultConfig
Definition config.hpp:31
constexpr const auto inf
Definition config.hpp:112
constexpr const char * enum_name(LBFGSStepSize s)
Definition lbfgs.hpp:229
Double-precision double configuration.
Definition config.hpp:174
static attribute_accessor make(A T_actual::*attr, std::string_view="")
Definition json.tpp:97
std::function< void(const any_ptr &, const json &)> set
Definition json.tpp:107
std::function< void(const any_ptr &, json &)> get
Definition json.tpp:108
Custom parameter parsing exception.
Definition json.hpp:28
std::vector< std::string > backtrace
Definition json.hpp:30