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