7

I'm new to both protocol buffers and C++, so this may be a basic question, but I haven't had any luck finding answers. Basically, I want the functionality of a dictionary defined in my .proto file like an enum. I'm using the protocol buffer to send data, and I want to define units and their respective names. An enum would allow me to define the units, but I don't know how to map the human-readable strings to that.

As an example of what I mean, the .proto file might look something like:

message DataPack {
    // obviously not valid, but something like this
    dict UnitType {
        KmPerHour = "km/h";
        MiPerHour = "mph";
    }

    required int id = 1;
    repeated DataPoint pt = 2;

    message DataPoint {
        required int id = 1;
        required int value = 2;
        optional UnitType theunit = 3;
    }
}

and then have something like to create / handle messages:

// construct
DataPack pack;
pack->set_id(123);
DataPack::DataPoint pt = pack.add_point();
pt->set_id(456);
pt->set_value(789);
pt->set_unit(DataPack::UnitType::KmPerHour);

// read values
DataPack::UnitType theunit = pt.unit();
cout << theunit.name << endl; // print "km/h"

I could just define an enum with the unit names and write a function to map them to strings on the receiving end, but it would make more sense to have them defined in the same spot, and that solution seems too complicated (at least, for someone who has lately been spoiled by the conveniences of Python). Is there an easier way to accomplish this?

user812786
  • 4,302
  • 5
  • 38
  • 50
  • 1st: This problem has nothing to do with C++, you should remove this tag. 2nd: I think that this is beyond protobuf's primary purpose to provide an efficient wire format for the data. I would go for the enum <-> readable string mapping solution. Though it would be nice to use a protobuf plugin, to generate the necessary mapping code e.g. using special comment tags. – πάντα ῥεῖ Jul 13 '12 at 16:37

2 Answers2

8

You could use custom options to associate a string with each enum member: https://developers.google.com/protocol-buffers/docs/proto#options

It would look like this in the .proto:

extend google.protobuf.FieldOptions {
  optional string name = 12345;
}

enum UnitType {
    KmPerHour = 1 [(name) = "km/h"];
    MiPerHour = 2 [(name) = "mph"];
}

Beware, though, that some third-party protobuf libraries don't understand these options.

jpa
  • 10,351
  • 1
  • 28
  • 45
  • 2
    This looks like exactly what I want, although I think I need to use `EnumValueOptions` rather than `FieldOptions`. Thanks. I'm having trouble accessing the result with C++ though - Could you perhaps give a short example of actually retrieving the values? – user812786 Jul 16 '12 at 17:50
  • 1
    FYI, I posted a question asking for an example of reading the values [here](http://stackoverflow.com/questions/11511822/read-value-of-enum-extension-in-protocol-buffer/11514690) and found a working solution. Thanks again for pointing me in the right direction! – user812786 Jul 17 '12 at 13:41
  • 2
    Do you why did the protobuf3 team omitted the ability to leave even string or int constants?? It's the most basic gimme and obvious feature of using .proto files to begin with -- to have a single source file for cross-platform message patterns. Argh! – Drew O'Meara Aug 10 '18 at 16:16
4

In proto3, it's:

extend google.protobuf.EnumValueOptions {
  string name = 12345;
}

enum UnitType {
  KM_PER_HOUR = 0 [(name) = "km/h"];
  MI_PER_HOUR = 1 [(name) = "mph"];
}

and to access it in Java:

UnitType.KM_PER_HOUR.getValueDescriptor().getOptions().getExtension(MyOuterClass.name);
Noel Yap
  • 18,822
  • 21
  • 92
  • 144