Calibration Library 1.0.0
A C++ library for camera calibration and vision-related geometric transformations
Loading...
Searching...
No Matches
json.h
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <boost/pfr/core.hpp>
5
6#if defined(__has_include)
7#if __has_include(<boost/pfr/core_name.hpp>)
8#include <boost/pfr/core_name.hpp>
9#define CALIB_HAS_PFR_CORE_NAME 1
10#endif
11#endif
12
13#include <concepts>
14#include <nlohmann/json.hpp>
15#include <optional>
16#include <string>
17#include <string_view>
18#include <type_traits>
19
20namespace calib {
21
22template <class T>
23concept serializable_aggregate = std::is_aggregate_v<T> && requires(T a, nlohmann::json j) {
24 { to_json(j, a) } -> std::same_as<void>;
25 { from_json(j, a) } -> std::same_as<void>;
26};
27
28namespace detail {
29template <class T>
30struct is_optional : std::false_type {};
31template <class U>
32struct is_optional<std::optional<U>> : std::true_type {};
33template <class T>
34inline constexpr bool is_optional_v = is_optional<std::decay_t<T>>::value;
35
36template <class Opt>
37using optional_value_t = typename std::decay_t<Opt>::value_type;
38
39inline std::string idx_key(std::size_t idx) {
40 using std::to_string;
41 return "field_" + to_string(idx);
42}
43} // namespace detail
44
45// ----------------------------
46// Aggregate → JSON
47// ----------------------------
48template <class T, std::enable_if_t<std::is_aggregate_v<T>, int> = 0>
49void to_json(nlohmann::json& j, const T& value) {
50 j = nlohmann::json::object();
51
52#if CALIB_HAS_PFR_CORE_NAME
53 constexpr auto names = boost::pfr::names_as_array<T>();
54 std::size_t idx = 0;
55
56 boost::pfr::for_each_field(value, [&](auto const& field) mutable {
57 const std::string i_key = detail::idx_key(idx);
58 const std::string n_key = std::string(names[idx]); // ← convert sv→string
59 const bool have_named = !n_key.empty();
60
61 if constexpr (detail::is_optional_v<decltype(field)>) {
62 if (field) {
63 j[i_key] = *field;
64 if (have_named) j[n_key] = *field;
65 }
66 } else {
67 j[i_key] = field;
68 if (have_named) j[n_key] = field;
69 }
70 ++idx;
71 });
72#else
73 std::size_t idx = 0;
74 boost::pfr::for_each_field(value, [&](auto const& field) mutable {
75 const std::string i_key = detail::idx_key(idx++);
76 if constexpr (detail::is_optional_v<decltype(field)>) {
77 if (field) j[i_key] = *field; // omit when nullopt
78 } else {
79 j[i_key] = field;
80 }
81 });
82#endif
83}
84
85// ----------------------------
86// JSON → Aggregate
87// ----------------------------
88template <class T, std::enable_if_t<std::is_aggregate_v<T>, int> = 0>
89void from_json(const nlohmann::json& j, T& value) {
90#if CALIB_HAS_PFR_CORE_NAME
91 constexpr auto names = boost::pfr::names_as_array<T>();
92 std::size_t idx = 0;
93
94 boost::pfr::for_each_field(value, [&](auto& field) mutable {
95 const std::string i_key = detail::idx_key(idx);
96 const std::string n_key = std::string(names[idx]); // ← convert sv→string
97 const bool have_named = !n_key.empty();
98
99 auto read_optional = [&](auto& opt) {
100 using Inner = detail::optional_value_t<decltype(opt)>;
101
102 // Prefer named key, then index key
103 const nlohmann::json* slot = nullptr;
104 if (have_named) {
105 if (auto it = j.find(n_key); it != j.end()) slot = &*it;
106 }
107 if (!slot) {
108 if (auto it = j.find(i_key); it != j.end()) slot = &*it;
109 }
110
111 if (!slot || slot->is_null()) {
112 opt.reset();
113 } else {
114 opt = slot->get<Inner>();
115 }
116 };
117
118 if constexpr (detail::is_optional_v<decltype(field)>) {
119 read_optional(field);
120 } else {
121 if (have_named && j.contains(n_key)) {
122 j.at(n_key).get_to(field);
123 } else if (j.contains(i_key)) {
124 j.at(i_key).get_to(field);
125 } else {
126 // Throw with the most user-friendly key we can show
127 j.at(have_named ? n_key : i_key).get_to(field);
128 }
129 }
130 ++idx;
131 });
132#else
133 std::size_t idx = 0;
134 boost::pfr::for_each_field(value, [&](auto& field) mutable {
135 const std::string i_key = detail::idx_key(idx++);
136
137 if constexpr (detail::is_optional_v<decltype(field)>) {
138 using Inner = detail::optional_value_t<decltype(field)>;
139 if (auto it = j.find(i_key); it == j.end() || it->is_null()) {
140 field.reset();
141 } else {
142 field = it->get<Inner>();
143 }
144 } else {
145 j.at(i_key).get_to(field); // required
146 }
147 });
148#endif
149}
150
151} // namespace calib
std::string idx_key(std::size_t idx)
Definition json.h:39
constexpr bool is_optional_v
Definition json.h:34
typename std::decay_t< Opt >::value_type optional_value_t
Definition json.h:37
Linear multi-camera extrinsics initialisation (DLT)
void from_json(const nlohmann::json &j, T &value)
Definition json.h:89
void to_json(nlohmann::json &j, const T &value)
Definition json.h:49