timthelion
timthelion

Reputation: 2727

Using protbuf3, how can I express the type 'Map string (Maybe CustomType)'?

I am trying to share a large dictionary/map between a client and a service. I need to be able to bidirectionally set values, and delete values from the dictionary/map without passing the entire map back and forth each time.

I know I can create a map using:

map<string, CustomType> cells = 3;

(Docs for protobuf3 maps)

I see a sugestion to use oneof here: https://github.com/google/protobuf/issues/1606

However, the syntax suggested in that bug report doesn't make sense to me:

message Test1 {
 oneof a_oneof {
  int32 a = 1;
 }
}

How would I test for that? How would I store None?

I would try to create something like:

message None{}
message MaybeCustomType{
 oneof maybe{
   CustomType just = 1;
   None       none = 2;
 }
}

But I'm not at all convinced that this is the elegant and correct solution.

I also considered changing my schema entirely and doing.

map<string, CustomType> cells = 1;
repeated string deleted_cells = 2;

However I don't like that solution because it creates an undefined case. What happens if a cell named "foo" appears in both the "cells" map and the "deleted_cells" map? Furthermore, it abstracts the data away from the computation. I'd like to be able to pass a cell to a function which modifies and potentially decides to delete the cell, and so it seems natural to me that information about cell deletion be stored near the cell itself.

Upvotes: 4

Views: 2414

Answers (1)

v1bri
v1bri

Reputation: 1428

Your language tag mentions haskell but I'm not sure which library implementation you're using to provide proto3 support, so I'll try to provide a generic answer.

Overview

The thread you pointed to suggests using oneof with a single field since it's possible to represent a null (really the absence of the field). Fortunately you don't need to create a None type for this purpose since it is built into the language-specific generated source files. Take a look at the Language Guide for oneof...

You can check which value in a oneof is set (if any) using a special case() or WhichOneof() method, depending on your chosen language.

The "if any" part is the magic you're looking for. The generated C++ code for a oneof definition will provide a special oneof_name_case() method that will tell you if one of the fields have been set...

OneofNameCase oneof_name_case() const: Returns the enum indicating which field is set. Returns ONEOF_NAME_NOT_SET if none of them is set.

A similar method is generated for Java code: getOneofNameCase() returns ONEOFNAME_NOT_SET if none of the oneof fields are set.

Example

Starting from a test message...

message Test1 {
    oneof single_field_oneof {
        int32 some_int = 1;
    }
}

If you were using C++ you could use code similar to the following to handle the oneof field...

int main(int argc, char* argv[]) {
    Test1 test;

    // <Populate message somehow>

    if (test.single_field_case() == Test1::kSomeInt) {
        std::cout << "Field is set, value is " << test.some_int() << std::endl;
    } else {
        assert(test.single_field_case() == Test1::SINGLE_FIELD_NOT_SET);
        std::cout << "Field is not set" << std::endl;
    }
}

Anyway, as I mentioned I'm not familiar with the specific proto3 language bindings for Haskell, but the oneof feature should have similar functionality in the library that you're using.

Upvotes: 1

Related Questions