'Proto2 optional field fails has_field call when set to 0

Using proto2 syntax I have an ElementTest message that I populate like this:

  for(int gain = 0; gain < 3; gain++)
    //for(int gain = 1; gain < 128; gain++)
  {
    elementTest->set_v_gain_tx(gain);
    elementTest->set_h_gain_tx(gain);
    elementTest->set_v_gain_rx(gain);
    elementTest->set_h_gain_rx(gain);
for(int phase = 0; phase < 3; phase++)
  //for(int phase = 1; phase < 128; phase++)
{
  elementTest->set_v_phase_tx(phase);
  elementTest->set_h_phase_tx(phase);
  elementTest->set_v_phase_rx(phase);
  elementTest->set_h_phase_rx(phase);

After populating all the fields I create a string and send via UDP to the consumer

std::string *execStr = new string();
oemTest.SerializeToString(reinterpret_cast<std::string *>(execStr));

On the receiving side I Parse the string

std::string *recvStr = new string(recvBuf);
gen5P::OemTest oemTest{};
oemTest.ParseFromString(*(reinterpret_cast<std::string *>(recvStr)));

Before using in the consumer I check that the field is present:

eT = oemTest.mutable_elementtest();

if(!(eT->has_v_phase_tx() && eT->has_h_phase_tx() && 
     eT->has_v_phase_rx() && eT->has_h_phase_rx()))

This works fine for all values except 0, when I set the gain fields to 0 some of the phase and gain fields fail, when I set phase to 0 all to the gain fields are present, but some of the phase fields are not. When I change to the fields from optional to required it works.

Is this the expected behavior?

syntax = "proto2";

package gen5P;


enum MsgTypes {
  eOemCommand = 1;
  eOemStatus = 2;
  eTerminateCommand = 3;
};

enum TestTypes {
  eElementTest = 1;
  eLoadTest = 2;
  eOther = 3;
}

enum eModePolarity {
    e_Hor_Tx    =  1;
    e_Hor_Rx    =  2;
    e_Hor_TxRx  =  3;
    e_Ver_Tx    =  4;
    e_Ver_Rx    =  5;
    e_Ver_TxRx  =  6;
    e_Both_Tx   =  7;
    e_Both_Rx   =  8;
    e_Both_TxRx =  9;
    e_Last      = 10;
};

message ElementTest {
    optional eModePolarity modePolarity = 1;    // Identifies Tx/Rx and Polarity
    optional uint32        element = 2;         // Allowable values 1 - 256
    optional uint32        v_gain_tx = 3;          // 0-127 7 bits
    optional uint32        h_gain_tx = 4;          // 0-127 7 bits
    optional uint32        v_gain_rx = 5;          // 0-127 7 bits
    optional uint32        h_gain_rx = 6;          // 0-127 7 bits
    optional uint32        v_phase_tx = 7;         // 0-127 7 bits
    optional uint32        h_phase_tx = 8;         // 0-127 7 bits
    optional uint32        v_phase_rx = 9;         // 0-127 7 bits
    optional uint32        h_phase_rx = 10;         // 0-127 7 bits
    optional bool          v_attenuation_tx = 11;   // TRUE if attenation is on 
    optional bool          h_attenuation_tx = 12;   // TRUE if attenation is on
    optional bool          v_attenuation_rx = 13;   // TRUE if attenation is on
    optional bool          h_attenuation_rx = 14;   // TRUE if attenation is on
};

message LoadTest {
    repeated uint32        txElements   = 1;        // List of Tx elements to enable
    repeated uint32        rxElements   = 2;        // List of Rx elements to enable
    optional uint32        protoIndex = 3;          // Number in the name of the Load proto
};

message OemTest {
    optional MsgTypes        msgType = 1;
    optional TestTypes       testType = 2;
    optional ElementTest     elementTest = 3;
    optional LoadTest        loadTest = 4;
};

enum Status {
    ePass = 1;
    eFail = 2;
};

    
message OemTestStatus {
    optional MsgTypes    msgType = 1;
    optional Status      status = 2;
};


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source