4

I want to have in a google protocol buffer repeated field only unique elements. In other words, need to use it as a std::set instead of std::vector.

Any idea which is the simplest and most efficient way to do that?

EDIT: I wouldn't want to use any iterators to loop through all the elements if possible.

rene
  • 41,474
  • 78
  • 114
  • 152
Alex
  • 409
  • 5
  • 12
  • 1
    Google protobufs don't support that granular of detail in their interfaces. You could modify the source code, but that seems like a giant waste of time. Iterating through all of the elements and adding them to a set is probably the best solution. – Josh Apr 08 '13 at 14:26
  • ...k, thnks, that confirm my findings :) – Alex Apr 08 '13 at 15:14
  • Are you constrained by google protobufs, or are you allowed to use a different toolkit for your code? – Josh Apr 08 '13 at 15:49
  • While gpb are very constrained and cannot create a precise api, I think there is a strength in this simplicity. It is very easy to look at a protobuf definition in clear text and understand exactly what it's doing. Try doing that with a complicated xml schema; you'll be lost for days! – Josh Apr 09 '13 at 13:24

2 Answers2

2

Ok, as the comments from the question stated, there isn't any way of doing this without using iterators. However, maybe someone else is interested in this, here is the function i coded to achieve this. This will take as parameters a RepeatedPtrField< T >*(the list) and a std::string(key of the new object that we intend to add to the list) and will return the element that matches the id, or NULL if there isn't any entry with this key in the RepeatedField list.

This way, you can easy keep a list of unique elements directly in a RepeatedField without using any other std structure:

template <class T>
T* repeatedFieldLookup( google::protobuf::RepeatedPtrField< T >* repeatedPtrField, std::string id)
{
   google::protobuf::internal::RepeatedPtrOverPtrsIterator<T> it = repeatedPtrField->pointer_begin();
   for ( ; it != repeatedPtrField->pointer_end() ; ++it )
   {
      CommonFields * commonMessage = (CommonFields*) (*it)->GetReflection()->
     MutableMessage ((*it), (*it)->GetDescriptor()->FindFieldByName ("common"));
      if(commonMessage->id() == id)
      {
     return *it;
      }
   }
   return NULL;
}

NOTE: in the example above, the proto message will ALWAYS have a field called common(which in my case is also a proto message). You can replace that by anything that you want to make the comparison from your proto messages.

Alex
  • 409
  • 5
  • 12
0

In the case where I had this class:

class Description : public ::google::protobuf::Message {
  // ...
  inline void add_field(const ::std::string& value);
  inline const ::google::protobuf::RepeatedPtrField< ::std::string>& field() const;
  // ...
};

I used std::find to only add a value if it didn't exist in the list:

#include <algorithm>

void addField(Description& description, const std::string& value) {
    const auto& fields = description.field();
    if (std::find(fields.begin(), fields.end(), value) == fields.end()) {
        description.add_field(value);
    }
}
kiulu79
  • 1
  • 2