> On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > Chun and I went over this together, so feel free to reach out to either of > > us for discussion! Just some suggestions to clean up the code below. > > > > In the description, could we mention that this is for stout's json <-> > > protobuf conversion? E.g. > > > > ``` > > Added protobuf map support to stout JSON<->protobuf conversion. > > > > Map is a feature of proto2 syntax, but it can only be compiled > > with 3.0.0+ protobuf compiler, see the following discussion for > > details: > > > > https://groups.google.com/forum/#!topic/protobuf/p4WxcplrlA4 > > > > We have already upgraded the compiler from 2.6.1 to 3.3.0 in > > MESOS-7228. This patch adds map support in the json <-> protobuf > > conversion in stout. > > ```
Sure, I have updated the commit message to mention it. > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 32 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line32> > > > > FWICT, we're just supposed to include message.h for reflection: > > > > > > https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message#Reflection > > > > And this reflection.h header seems internal and already included by > > message.h? It only seems to define some pieces of the reflection code: > > > > > > https://github.com/google/protobuf/blob/v3.5.0/src/google/protobuf/reflection.h You are right! Previous I called `reflection->GetRepeatedFieldRef()` which needs `reflection.h`: https://github.com/google/protobuf/blob/v3.5.0/src/google/protobuf/message.h#L787:L792 But later I changed the solution by calling `reflection->AddMessage()`, so I should have removed it from the code, thanks for catching this! > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 388 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line388> > > > > s/name/key/? I'd like to be consistent with the existing code: https://github.com/apache/mesos/blob/1.5.0/3rdparty/stout/include/stout/protobuf.hpp#L575:L576. And in another hand, in the code below, I already have another local variable named `key`, e.g.,: ``` Try<int64_t> key = numify<int64_t>(name); ``` So I'd like to differentiate them. > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 391-487 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line391> > > > > It looks like we can avoid this logic since protobuf's JSON conversion > > allows the protobuf key types (bool, integers, strings) to be converted > > from JSON strings. This means we could just recurse for both key and value > > here: > > > > ``` > > // Some comment explaining what map is equivalent to with > > // a reference to the google documentation. > > google::protobuf::Message* entry = > > reflection->AddMessage(message, field); > > > > const google::protobuf::FieldDescriptor* key_field = > > entry->GetDescriptor()->FindFieldByNumber(1); > > > > // Maybe passing just 'key' works due to implicit > > construction? > > // TODO(...): This copies the key, consider avoiding this. > > Try<Nothing> apply = > > boost::apply_visitor(Parser(entry, key_field), > > JSON::String(key)); > > > > if (apply.isError()) { > > return Error(apply.error()); > > } > > > > const google::protobuf::FieldDescriptor* value_field = > > entry->GetDescriptor()->FindFieldByNumber(2); > > > > Try<Nothing> apply = > > boost::apply_visitor(Parser(entry, value_field), value); > > > > if (apply.isError()) { > > return Error(apply.error()); > > } > > ``` > > > > Now, to make this simplification work, we need to update our JSON > > conversion in a separate patch to allow bools and integers to be parsed > > from JSON strings to match google's logic for conversion: > > > > > > https://github.com/google/protobuf/blob/v3.5.1/src/google/protobuf/util/internal/datapiece.cc#L203 > > > > Documentation here: > > https://developers.google.com/protocol-buffers/docs/proto3#json > // Maybe passing just 'key' works due to implicit construction? We cannot pass `key` or even `JSON::String` to `boost::apply_visitor()` because it cannot pass compilation: ``` ../3rdparty/boost-1.65.0/boost/variant/detail/apply_visitor_unary.hpp:84:22: error: no member named 'apply_visitor' in 'std::__1::basic_string<char>' ``` or ``` ../3rdparty/boost-1.65.0/boost/variant/detail/apply_visitor_unary.hpp:84:22: error: no member named 'apply_visitor' in 'JSON::String' ``` That is because `std::string` and `JSON::String` do not have a member `apply_visitor`, so I think we have to use `JSON::Value` like this: ``` google::protobuf::Message* entry = reflection->AddMessage(message, field); const google::protobuf::FieldDescriptor* key_field = entry->GetDescriptor()->FindFieldByNumber(1); JSON::Value key(name); Try<Nothing> apply = boost::apply_visitor(Parser(entry, key_field), key); if (apply.isError()) { return Error(apply.error()); } const google::protobuf::FieldDescriptor* value_field = entry->GetDescriptor()->FindFieldByNumber(2); apply = boost::apply_visitor(Parser(entry, value_field), value); if (apply.isError()) { return Error(apply.error()); } ``` > we need to update our JSON conversion in a separate patch to allow bools and > integers to be parsed from JSON strings to match google's logic for conversion Did you mean we should improve the method below by adding more cases for converting `JSON::String` to bools and integers? ``` Try<Nothing> operator()(const JSON::String& string) const { switch (field->type()) { case google::protobuf::FieldDescriptor::TYPE_STRING: ... ``` If so, then that means moving the code here (see below) into the above method. But I think those code are specific for map support, however `Try<Nothing> operator()(const JSON::String& string) const` is a generic method for JSON string conversion, so I would still like to keep those code as where they are now in this patch (i.e., the code path to handle map). ``` case google::protobuf::FieldDescriptor::TYPE_INT64: case google::protobuf::FieldDescriptor::TYPE_SINT64: case google::protobuf::FieldDescriptor::TYPE_SFIXED64: { Try<int64_t> key = numify<int64_t>(name); ... } case google::protobuf::FieldDescriptor::TYPE_UINT64: case google::protobuf::FieldDescriptor::TYPE_FIXED64: { Try<uint64_t> key = numify<uint64_t>(name); ... } ... ``` > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 394-398 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line394> > > > > Can we reference the documentation here and mention that a map is > > equivalent to: > > > > ``` > > message MapFieldEntry { > > optional key_type key = 1; > > optional value_type value = 2; > > } > > > > repeated MapFieldEntry map_field = N; > > ``` Sure. > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 400-401 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line400> > > > > Can you move this out of the loop? It needs the variable `entry` which is defined inside of the `foreachpair` loop, so I think we still need to keep `reflection` inside of the loop. > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 1036-1037 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line1037> > > > > Google converts 64 bit integers into strings, it's pretty badly broken > > right now that we don't do this. I think we're going to have to switch to > > string and just deal with breaking any clients that assumed strings were > > not coming out. I don't know if you want to help fix this, but if you do I > > would be happy to review / discuss! Yeah, in the doc below, I see the 64 bit integers in protobuf message will be converted into JSON strings. https://developers.google.com/protocol-buffers/docs/proto3#json How about I create a separate ticket for it and fix it there? > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 1039-1043 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line1040> > > > > Is there a way to abstract out this logic into a lambda here and re-use > > it? E.g. > > > > ``` > > template <typename T> > > JSON::Value value_for_field( > > Message* entry, > > google::protobuf::Reflection* reflection, > > google::protobuf::FieldDescriptor* field) > > { > > switch (type) { > > case ...: > > case TYPE_INT64: > > return ...; > > } > > }; > > ``` > > > > Then you just do the following here and elsewhere: > > > > ``` > > map.values[key] = value_for_field(entry, reflection, field); > > ``` > > > > I've glossed over some details, but I hope you get the idea and can > > figure out how to make it work across the different cases here. Do you mean moving the big `"switch ... case"` introduced in my patch for map into a method `value_for_field()`, and use it to replace the other two big `"switch ... case"` for array and object (see below) too? https://github.com/apache/mesos/blob/1.5.0/3rdparty/stout/include/stout/protobuf.hpp#L864:L923 https://github.com/apache/mesos/blob/1.5.0/3rdparty/stout/include/stout/protobuf.hpp#L927:L986 I like the idea and I think it is doable for map and object, but for array, it is a bit different since we will call different protobuf APIs, e.g., `reflection->GetInt32` (map and object) v.s. `reflection->GetRepeatedInt32()` (array). So I think it may not be able to cover array :-( - Qian ----------------------------------------------------------- This is an automatically generated e-mail. To reply, visit: https://reviews.apache.org/r/59987/#review198429 ----------------------------------------------------------- On March 1, 2018, 7:27 a.m., Qian Zhang wrote: > > ----------------------------------------------------------- > This is an automatically generated e-mail. To reply, visit: > https://reviews.apache.org/r/59987/ > ----------------------------------------------------------- > > (Updated March 1, 2018, 7:27 a.m.) > > > Review request for mesos, Anand Mazumdar, Benjamin Mahler, Chun-Hung Hsiao, > and Zhitao Li. > > > Bugs: MESOS-7656 > https://issues.apache.org/jira/browse/MESOS-7656 > > > Repository: mesos > > > Description > ------- > > Map is a feature of proto2 syntax, but it can only be compiled > with proto3 compiler, see the following discussion for details: > https://groups.google.com/forum/#!topic/protobuf/p4WxcplrlA4 > We have already upgraded the compiler from 2.6.1 to 3.3.0 in > MESOS-7228, however, to use protobuf map in Mesos code, we also > need to add the protobuf map support to the code in Mesos for > converting protobuf message to JSON object and parsing JSON > object as protobuf message, that is what I have done in this patch. > > > Diffs > ----- > > 3rdparty/stout/include/stout/protobuf.hpp > 4a1605e5130dbf7e8286dbb43d0d04ab4394e79a > > > Diff: https://reviews.apache.org/r/59987/diff/5/ > > > Testing > ------- > > > Thanks, > > Qian Zhang > >