C++ Protobufs :: How to erase particular field with MergeFrom()? - c++

First of all: I'm not an expert in protobuf.
Suppose I have such message structure:
package msg_RepAndOpt;
message RepAndOpt
{
repeated string name = 1;
optional string surname = 2;
...
// there are lots of others.
}
And I have two components that have copies of this message:
// component1:
RepAndOpt A;
A.add_name("Name");
A.set_surname("Surname");
// component2:
RepAndOpt B;
In my case components modify those messages via transaction mechanism. It means that if one component changes some field it also sends it to another component to propagate those changes. Component-receiver is doing merge:
// Component2 modifies B and sends it to component1.
// Component1 perfoms merge:
A.MergeFrom(B);
Now, say, component2 wants to erase field "name".
If it will send clear B message (default construction) than:
MergeFrom() will not modify A;
CopyFrom() will erase also other fields.
Another way will be to fill B with the contents of A, clear name field and component1 will use CopyFrom().
But this is not acceptable because system is really high-loaded and there could be lots of other fields.
So, desired solution to clean name field is:
Component2 create B message.
Explicitly stores information that it want to erase only name field.
Component1 perform A.MergeFrom(B).
Result: A::name is cleared but other fields are left untouched.
As far as I tested this applies to repeated and optional fields.
Is there any ready-to-use solution or I should modify protobuf implementation?

There is no builtin protobuf solution for your case. The obvious solution would be to iterate over all the fields in message A and check if that field is present in message B, if not you could clear it.

You can't solve this with basic MergeFrom(), but you may want to check out these from the protobuf library:
https://github.com/google/protobuf/blob/master/src/google/protobuf/field_mask.proto
https://github.com/google/protobuf/blob/master/src/google/protobuf/util/field_mask_util.h
In particular FieldMaskUtil::MergeMessageTo() seems to do what you want. You'll need to construct a FieldMask specifying exactly which fields you're interested in, so that other fields are left untouched.

UPD: updated after comments from Kenton Varda (see below).
Expanding one of the previous answers:
There is a way to solve it by adding new field in message definition (this works for proto v2):
repeated int32 fields_to_copy = 15;
This field will be filled by ID's of fields that would be copied (not merged) on the reciever side.
I also implemented this helper function:
// CopiableProtoMsg.hpp
#pragma once
#include <google/protobuf/message.h>
template <typename T>
void CopyMessageFields(const T& from, T& to)
{
const ::google::protobuf::Descriptor *desc = T::descriptor();
const ::google::protobuf::Reflection *thisRefl = from.GetReflection();
std::vector<const ::google::protobuf::FieldDescriptor*> fields;
int size = from.fields_to_copy_size();
for (int i = 0; i < size; ++i)
{
const ::google::protobuf::FieldDescriptor *field = desc->FindFieldByNumber(from.fields_to_copy(i));
fields.push_back(field);
}
T msgCopy(from);
thisRefl->SwapFields(&to, &msgCopy, fields);
to.clear_fields_to_copy();
}
This function checks fields_to_copy field and perform copying (via SwapFields()).
Here is the simple test:
RepAndOpt emulateSerialization(const RepAndOpt& B)
{
RepAndOpt BB;
std::string data;
B.SerializeToString(&data);
BB.ParseFromString(data);
return BB;
}
TEST(RepAndOptTest, additional_field_do_the_job_with_serialization)
{
RepAndOpt A;
RepAndOpt B;
A.add_name("1");
A.add_name("2");
A.add_name("3");
A.set_surname("A");
B.add_name("1");
B.add_name("3");
B.add_fields_to_copy(RepAndOpt::kNameFieldNumber);
RepAndOpt recvB = emulateSerialization(B);
A.MergeFrom(recvB);
CopyMessageFields(recvB, A);
EXPECT_EQ(2, A.name_size());
EXPECT_STREQ("1", A.name(0).c_str());
EXPECT_STREQ("3", A.name(1).c_str());
EXPECT_TRUE(A.has_surname());
EXPECT_EQ(0, A.fields_to_copy_size());
}

Expanding approach with field masks (that was proposed by Kenton Varda):
Note: This solution requires proto3, however the original message can be declared with proto2 syntax. (link to proof)
We can define a field mask field:
import "google/protobuf/field_mask.proto";
message RepAndOpt
{
repeated string name = 1;
optional string surname = 2;
optional google.protobuf.FieldMask field_mask = 3;
}
And here is the test usage:
RepAndOpt emulateSerialization(const RepAndOpt& B)
{
RepAndOpt BB;
std::string data;
B.SerializeToString(&data);
BB.ParseFromString(data);
return BB;
}
void mergeMessageTo(const RepAndOpt& src, RepAndOpt& dst)
{
dst.MergeFrom(src);
if (src.has_field_mask())
{
FieldMaskUtil::MergeOptions megreOpt;
megreOpt.set_replace_message_fields(true);
megreOpt.set_replace_repeated_fields(true);
FieldMaskUtil::MergeMessageTo(src, src.field_mask(), megreOpt, &dst);
}
}
TEST(RepAndOptTest, fix_merge_do_the_job_with_serialization_multiple_values)
{
RepAndOpt A;
A.add_name("A");
A.add_name("B");
A.add_name("C");
A.set_surname("surname");
RepAndOpt B;
B.add_name("A");
B.add_name("C");
B.mutable_field_mask()->add_paths("name");
mergeMessageTo(emulateSerialization(B), A);
EXPECT_EQ(2, A.name_size());
EXPECT_STREQ("A", A.name(0).c_str());
EXPECT_STREQ("C", A.name(1).c_str());
EXPECT_STREQ("surname", A.surname().c_str());
}

I had similar use case and ended up implement my own mixture based on that #Denis' answer.
Although the language was Golang, which doesn't have FieldMaskUtil with MergeOptions.
RepAndOpt.A.name RepAndOpt.B.name
remove ["A", "B", "C"] ["A"] => remove: A, keep: B, C
add ["A", "B", "C"] ["D"] => add: D, keep: A, B, C
add/remove ["A", "B", "C"] ["A", "D"] => remove: A, add: D, keep: B

Related

whenNew in Kotlin unit test & Missing calls inside every block

Specifically asking, is there a Kotlin version for this java statement?
whenNew(myClass.class).withAnyArguments().thenReturn(myObject);
I have looked everywhere and haven't found anything that has worked for me yet.
I have tried:
every { myClass(any(), any()) } returns myObject
To this I get this error - argumentA must be in the form m.n. This tells me that blank arguments are being fed to myClass constructor but this is not my motive. I want this class to always return myObject whenever a constructor is being called, irrespective of the arguments.
I have also tried this:
every { myClass(TEST_A, TEST_B) } returns myObject
Then I get this error - Missing calls inside every { ... } block.
Another key point to add is - this is not the class I am writing the test for. I am writing the test cases for myOtherClass (actually defined in kotlin as an object, see below). Within one of the methods in myOtherClass, myClass is instantiated by feeding a set of arguments to its public constructor.
object myOtherClass {
fun mainFunction(a: A, b: B): X {
val answer = getAnswer(a, b)
y = convertAnswerToX(answer)
return y
}
private fun getAnswer(a: A, b: B): myClass {
val a1 = doSomethingToA(a)
val b1 = doSomethingToB(b)
return myClass(a1, b1)
}
}

How to get a field object using its name in proto3

I am migrating schemas from proto2 to proto3 syntax. I want to eliminate extensions as they are not supported. Is it possible to get an object using a field name in proto3, similar to what MutableExtension does in proto2.
For example,
Schema in proto2 syntax
message Foo {
message Bar {
unint32 a = 1;
}
extend Foo {
Bar b = 1;
}
}
C++
Foo::Bar b_val = foo.MutableExtension(Foo::b);
Now in proto3, I could do this:
syntax="proto3";
message Foo {
message Bar {
unint32 a = 1;
}
Bar b = 1;
}
C++ code:
Foo::Bar b_val = foo.mutable_b();
However, I want to use the name Foo::b to get a Foo::Bar object. Is there a way to do this?
It's not clear why you need this but what you are asking for is kinda feasible.
Foo::b is a garden variety member function, which means that &Foo::b is a regular pointer-to-member-function.
So you can use it as such using the regular c++ syntax for these entities:
auto b_ref = &Foo::b;
Foo::Bar b_val = (foo.*b_ref)();

How do you write a setter for a single item in a list (not the whole list)?

I have a class in my model that includes a list of bool. From my UI I want to set the bool state in just a single item in the list via a setter (so that I can also save it). I can't figure out the syntax (or whether this is a valid thing to do).
///This is OK
set notificationDismissed(bool notificationDismissed){
_notificationDismissed = notificationDismissed;
saveParameterBoolean(_notificationDismissedKey,
_notificationDismissed);
}
bool get notificationDismissed => _notificationDismissed;
///This is OK too
List<bool> get questionsAnswered => _questionsAnswered;
set questionsAnswered(List<bool> questionsAnswered){
_questionsAnswered = questionsAnswered;
for(int i=0; i<_questionAnsweredParamKeys.length; i++ ){
saveParameterBoolean(_questionAnsweredParamKeys[i],
_questionsAnswered[i]);
}
updateState();
}
///This is not OK !!!! but should show what I want to do
List<bool> get questionsAnswered[index] => _questionsAnswered[index];
set questionsAnswered[index](bool questionsAnswered[index]){
_questionsAnswered[index] = questionsAnswered[index];
saveParameterBoolean(_questionAnsweredParamKeys[index],
_questionsAnswered[index]);
updateState();
}
I know I'm missing something obvious here, any help greatly appreciated
get and set functions can't take any arguments. The simplest approach would be to use normal functions:
bool getQuestionsAnswered(int index) => _questionsAnswered[index];
void setQuestionsAnswered(int index, bool value) {
_questionsAnswered[index] = value;
saveParameterBoolean(_questionAnsweredParamKeys[index], _questionsAnswered[index]);
updateState();
}
Another alternative would be to change _questionsAnswered from a List to a custom class that implements operator [] (to get an element) and operator []= (to set an element), and then you could make them do whatever you want.

How to get list of values using criteria API

I have Entity classes as below. I am using open jpa.
Class RootClass{
A a;
}
Class A{
List<B> b;
}
Class B {
C c;
}
Class C {
String name;
}
When b was single object instead of list, I used the following statement to
select name. It was working fine.
Root<RootClass> rootElement = myQuery.from(RootClass.Class);
... ...
Predicate predicate = criteriaBuilder.equal(rootElement.get("a").get("b").get("c"). <String>get("name"),userName);
But when b is changed as a List, the above code doesn't work. Please provide some pointers on how to get list using Root.
What you need is join instead of get:
Root<RootClass> rootElement = myQuery.from(RootClass.Class);
...
Predicate predicate = criteriaBuilder.equal(rootElement.join("a").join("b").
get("c").<String>get("name"), userName);

How to compare special fields in google mock?

I have got question connected with google test. I would like to ask if while inducing "EXPECT_CALL(*A, some_method(mes1));" in test case there is possiblity to compare fields included
in mes1 class.
struct Mes
{
int a;
};
//short section of test case:
Mes mes1 = Mes();
EXPECT_CALL(*A, some_method(mes1));
I would like to ask if in google mock is a possiblity to compare special fields included in Mes. Something like:
EXPECT_CALL(*A, some_method(mes1), compare(Mes.a));//in this case google mock would compare only field "a" from Mes.
It depends on what you want to do with the result of the comparison. If you simply want to query the value of the field, you can simply define a function to do something with it:
// Note: The signature of someFunction needs to match some_method.
void someFunction(const Mes& mes)
{
// Do something with mes.a
}
Then set up your expectation as follows:
EXPECT_CALL(*A, some_method(mes1)).WillOnce(Invoke(someFunction));
Note that if some_method returns a value, you may also have to provide a Return action.
Alternatively, if you want your test to fail if the field isn't some specific value, you need to write a custom matcher:
MATCHER_P(MesFieldEq, val, "")
{
return (arg.a == val);
}
Then use it in your expectation as follows:
// Fails unless mes.a is equal to 42.
EXPECT_CALL(*A, some_method(MesFieldEq(42));