Repository navigation
Reading JSON into a struct with a DefaultVal field and a size validator field always raises a size validation error #700
Description
Activity
@wbernoudy, yes, I am aware of this issue and at the moment, i can't really think of a good way to fix it.
Here is what is happening: For normal parsing, we use this little trick:
alignas(T) unsigned char buf[sizeof(T)]{}; auto ptr = internal::ptr_cast<T*>(&buf); auto view = ProcessorsType::template process<T>(to_view(*ptr)); using ViewType = std::remove_cvref_t<decltype(view)>; const auto [set, err] = Parser<R, W, ViewType, ProcessorsType>::read_view(_r, _var, &view);
This allows us to read into the struct without actually initializing it. But when the struct has default values, we need to be able to use them, so we have to do this instead:
auto t = T{}; // This might fail, for instance if the default value does // not satisfy the validator, but in that case we will just // return the error. auto view = ProcessorsType::template process<T>(to_view(t)); using ViewType = decltype(view); const auto err = Parser<R, W, ViewType, ProcessorsType>::read_view_with_default( _r, _var, &view);
And as the comment indicates, we ran into this issue before. Honestly, I can't really think of a good way to get around this.
But if you have an idea, I am always open to suggestions.
Thank you @liuzicheng1987, this context was very helpful for understanding what is going on.
I thought about this for a while and the best I can come up with is using
std::source_locationto change the behavior of the default constructor ofrfl::Validatorto skip validation when it is being called from the parser specifically. Obviously this is quite "hacky", but I thought that since the library already relies onstd::source_location, it might be acceptable.I will open a PR soon with an implementation so you can see what I mean.
Hm one could disable validation for default constructed values here:
reflect-cpp/include/rfl/Validator.hpp
Line 44 in 4d99e54
Validator() : value_(ValidationType::validate(T()).value()) {} I dont know what happens when we parse a struct with a missing field that has a validator: Is the default constructor called then? And could be detect that it is a Validator and pass a
rfl::default_construct_tagto the constructor?Note that the issue is even more serious than a mere "raises an exception" as Result.hpp throws std::runtime_exceptions in several functions while the call stack may have noexcept functions on top (at least Parser_default.hpp does). So expect abort()s on fairly innocent use cases.
The main use case where I've struggled with this issue is a simple one: deserialize JSON that is not exactly compatible with the C++ structure instances to be populated. It seems to suffice to simply have JSON data that lacks a key-value pair mapped into a C++ structure. I've tried a remedy of using something like rfl::DefaultVal<My_Type> myMember{"MY_STRING"}; but that a) doesn't prevent the abort()s, and b) is against the use case idea of "this value has no sensible default - if it's missing from the deserialize data then throw user code catchable error". Note that a declaration like My_Type myMember; where the underlying type is std::string suffers from similar issues.
Oh and the rfl::Pattern also plays a part in this scheme, but I fear it's not the only class suffering from the same disease. Something like:
using My_Type = rfl::Pattern<R"((?:FirstVal|SecondVal)$)", "My_Type">;
So, the [noexcept ... throw] is definitely a defect that should be addressed.
For example, using
rfl::json::readwith the structseems to always fail with the exception
Size validation failed: Value expected to be greater than or equal to 1, but got 0., even when given a list offavorite_foodswith one or more items.A short test case to see the behavior:
However, if you set a default value for the field that satisfies the validator, everything seems to work. For example, you can change the
favorite_foodsdefinition in the above test toand it should pass.