Let's start with the following thrift:
struct SmallContainer {
1: string name
}
struct BigContainer {
1: SmallContainer c1,
2: SmallContainer c2
}
struct test {
1: map <string, BigContainer> myMap
}
The autogenerated class has problems in serializing the map, obtaining a serialization that screws up parentheses.
With the help of @drdanz we figured out the problem is due to the nesting of the value of the map (BigContainer is a struct containing two structs).
Digging in the code, here is what I found. Maps are handled by:
And the relevant part of the generate_serialize_field is line 2445 of
In this case generate_serialize_struct accepts a force_nesting flag which is not set for maps. A possible solution would be transforming
to
generate_serialize_field(out, &vfield, "", "", true);
This seems working also with non-nested structs, but a more thorough testing would be needed. I'm not an expert on this, and I don't know if this would affect other use cases.
The autogenerated code is the following:
bool test::write_myMap(const yarp::os::idl::WireWriter& writer) const {
{
if (!writer.writeMapBegin(BOTTLE_TAG_STRING, BOTTLE_TAG_LIST, static_cast<uint32_t>(test.size()))) return false;
std::map<std::string, BigContainer> ::const_iterator _iter322;
for (_iter322 = test.begin(); _iter322 != test.end(); ++_iter322)
{
if (!writer.writeListBegin(0,2)) return false;
if (!writer.writeString(_iter322->first)) return false;
if (!writer.write(_iter322->second)) return false; // Here is the error
if (!writer.writeListEnd()) return false;
}
if (!writer.writeMapEnd()) return false;
}
return true;
}
The line that writes the map's value should be:
if (!writer.writeNested(_iter322->second)) return false;
Thanks for the very detailed analysis.
I'm not sure if forcing the nested is always, the right thing to do, when second is not a struct, perhaps it should be serialized normally... We can probably check it whith one of these methods:
Have you checked if by applying your fix, the deserialization part is correct?
The thrift of yarp has another problem related to the pointclouds.
When it is sent as Bottle (after the call of toBottle), on the receiver side it comes as a Bottle of 1 element that it is a list containing the N points.
Without thrift the Bottle received contains N elements as expected(it is tested in yarp unit tests)
@fbottarel has better insight in this issue.
Probably we should open another issue, but I think it is another problem in the de-serialization
I'm not sure if forcing the nested is always, the right thing to do, when second is not a struct, perhaps it should be serialized normally
Actually, the write nested might be already doing this, we should check
@drdanz I dug a bit more on this problem which is blocking for me at the current moment. Lists and maps are fairly similar, but if I understood correctly lists always set force_nested=true both for serializing and deserializing. For this reason they always work.
So here what I see is:
maps to lists and always enable the nesting flagI don't have the bandwidth to fill a PR complete with unit tests but I can submit a hot fix.
Have you checked if by applying your fix, the deserialization part is correct?
From preliminary tests, editing in the same way the deserializing methods, yes.
if I understood correctly lists always set force_nested=true both for serializing and deserializing
In this case I'm ok with forcing it for maps as well...
Just to reference the old issue (on which I did not provide any information): https://github.com/robotology/yarp/issues/1143
I closed #1143 in favour of this issue that contains a lot of extra information. Thanks for pointing that out.
Most helpful comment
Just to reference the old issue (on which I did not provide any information): https://github.com/robotology/yarp/issues/1143