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;
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.
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.
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.
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.