Can I use both custom reader and optional at the same time?
I want to use both custom reader and optional at the same time.
In the code below, the custom reader works fine but can't properly handle null values.
Is there a way to make both of these features work correctly?
#include <string>
#include <iostream>
#include <optional>
#include "glaze/glaze.hpp"
struct TestVal {
std::optional<char> val;
};
template<>
struct glz::meta<TestVal> {
static constexpr auto read_val =[](TestVal& v, const std::string& input) {
if (input.empty()) {
v.val = std::nullopt;
return;
}
v.val = input[0];
};
static constexpr auto value = glz::object("val", glz::custom<read_val, nullptr>);
};
int main() {
// Expected: print 'A'
{
std::string src = R"({"val":"ABC"})";
TestVal v;
auto ec = glz::read_json(v, src);
if (ec) {
std::cout << "Read failed\n";
} else {
std::cout << "Read successful: " << v.val.value_or('?') << '\n';
}
}
// Expected: print '?'
{
std::string src = R"({"val":null})";
TestVal v;
auto ec = glz::read_json(v, src);
if (ec) {
std::cout << "Read failed " << glz::format_error(ec, src) << '\n';
} else {
std::cout << "Read successful: " << v.val.value_or('?') << '\n';
}
}
return 0;
}
Result:
Read successful: A
Read failed 1:8: expected_quote
{"val":null}
^
I think I've looked into this before and it isn't trivial to implement efficiently. But, I would like to look into this again, so I'll keep this issue open until we can resolve it.
@stephenberry Thank you for your consideration.
Since I understand the complexity of implementing this, I'll proceed with another approach for now. Even as it so, this library is already extremely useful.
Look at this
If you make your custom functions take a glz::generic then you can use all kinds of complex logic to handle reading/writing your optional value. This allows you to have custom handling for null values.
See examples in this pull request: #2080
Here's an example, where the member functions could also be external lambda functions in the glz::meta
struct generic_optional_int {
std::optional<int> num;
void read_val(const glz::generic& value) {
if (value.is_null()) {
num = std::nullopt;
return;
}
if (value.is_number()) {
num = value.as<int>();
return;
}
if (value.is_string()) {
const auto& str = value.get<std::string>();
if (str.empty()) {
num = std::nullopt;
return;
}
int val{};
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), val);
if (ec == std::errc{} && ptr == str.data() + str.size()) {
num = val;
return;
}
}
num = std::nullopt;
}
glz::generic write_val() const {
if (num.has_value()) {
return num.value();
}
return nullptr;
}
};
template <>
struct glz::meta<generic_optional_int> {
using T = generic_optional_int;
static constexpr auto value = glz::object(
"num", glz::custom<&T::read_val, &T::write_val>
);
};
I'm closing this issue because I think this should solve the original request.