использование отражения protobuf для получения дескриптора логического поля в С++

Я использую grpc в приложении C++. На стороне клиента мне нужно рефлексивно запросить логическое значение из сообщения, используя отражение.

Ответное сообщение показано ниже. К сожалению, я не вижу дескрипторы полей для логического поля mAP, однако я вижу дескрипторы полей для строковых полей. Что я делаю неправильно?

Джон

message Response {
    // OTP Connection Status
    enum OTPConnStatus {
        Disconnected              = 0;
        Connected                 = 1;
        InvalidCS                 = 2;
        DiscRequest               = 3;
    }

    // define the fields
    RXMessageType mMessageType    = 1;
    bool mAP                      = 2;
    OTPConnStatus mCS1            = 3;
    OTPConnStatus mCS2            = 4;
    OTPConnStatus mCS3            = 5;
    OTPConnStatus mCS4            = 6;
    string mOTP1                  = 7;
    string mOTP2                  = 8;
    string mOTP3                  = 9;
    string mOTP4                  = 10;
}
const auto reflection = pMessage->GetReflection();
std::vector<const FieldDescriptor*> fields;
pMessage->GetReflection()->ListFields(*pMessage, &fields);
const auto fieldIter = std::find_if(fields.cbegin(), fields.cend(),
    [&lcFieldName](const FieldDescriptor* next) {
        return boost::iequals(next->name(), lcFieldName);
    });
if (fieldIter != fields.cend()) {
    std::string result;
    auto fieldDescriptor = *fieldIter;
    if (!fieldDescriptor->is_repeated()) {
        switch (fieldDescriptor->cpp_type()) {
        case FieldDescriptor::CPPTYPE_INT32:
            result = std::to_string(reflection->GetInt32 (
                *pMessage, fieldDescriptor));
            break;
        case FieldDescriptor::CPPTYPE_INT64:
            result = std::to_string(reflection->GetInt64 (
                *pMessage, fieldDescriptor));
            break;
        case FieldDescriptor::CPPTYPE_UINT32:
            result = std::to_string(reflection->GetUInt32 (
                *pMessage, fieldDescriptor));
            break;
        case FieldDescriptor::CPPTYPE_UINT64:
            result = std::to_string(reflection->GetUInt64 (
                *pMessage, fieldDescriptor));
            break;
        case FieldDescriptor::CPPTYPE_DOUBLE:
            result = std::to_string(reflection->GetDouble (
                *pMessage, fieldDescriptor));
            break;
        case FieldDescriptor::CPPTYPE_FLOAT:
            result = std::to_string(reflection->GetFloat (
                *pMessage, fieldDescriptor));
            break;
        case FieldDescriptor::CPPTYPE_BOOL:
            result = reflection->GetBool(
                *pMessage, fieldDescriptor) ?
                "true" : "false";
            break;
        case FieldDescriptor::CPPTYPE_ENUM:
            result = reflection->GetEnum(
                *pMessage, fieldDescriptor)->
                    full_name();
            break;
        case FieldDescriptor::CPPTYPE_STRING:
            result = reflection->GetString(
                *pMessage, fieldDescriptor);
            break;
        case FieldDescriptor::CPPTYPE_MESSAGE:
            //result = reflection->GetMessage(
            //    *pMessage, fieldDescriptor);
            break;
        }
    }
    std::cout << result << std::endl;
    exit(0);
} else {

РЕДАКТИРОВАТЬ: я нашел утилиту в protobuf, используемую для печати сообщений, которые я использовал для печати содержимого. Вот результаты:

const auto reflection = pMessage->GetReflection();
std::string formatted;
pb::TextFormat::PrintToString(*pMessage, &formatted);
std::cout << formatted;

Напечатано:

mMessageType: OneTimePassword
mOTP1: "TAILNO1"
mOTP2: "TAILNO2"
mOTP3: "TAILNO3"
mOTP4: "TAILNO4"

Также обратите внимание, что логическое поле в вопросе «карта» устанавливается сервером. Глядя на сгенерированный код CAService.pb.h и сравнивая MessageType (у которого есть рабочий дескриптор поля) с полем mAP, у которого его нет, может быть полезно пользователям protobuf, чтобы показать мне ошибку в моем подходе.

// optional .ca.RXMessageType mMessageType = 1;
inline void OTPResponse::clear_mmessagetype() {
  mmessagetype_ = 0;
}
inline ::ca::RXMessageType OTPResponse::mmessagetype() const {
  // @@protoc_insertion_point(field_get:ca.OTPResponse.mMessageType)
  return static_cast< ::ca::RXMessageType >(mmessagetype_);
}
inline void OTPResponse::set_mmessagetype(::ca::RXMessageType value) {

  mmessagetype_ = value;
  // @@protoc_insertion_point(field_set:ca.OTPResponse.mMessageType)
}

// optional bool mAP = 2;
inline void OTPResponse::clear_map() {
  map_ = false;
}
inline bool OTPResponse::map() const {
  // @@protoc_insertion_point(field_get:ca.OTPResponse.mAP)
  return map_;
}
inline void OTPResponse::set_map(bool value) {

  map_ = value;
  // @@protoc_insertion_point(field_set:ca.OTPResponse.mAP)
}

person johnco3    schedule 18.04.2016    source источник
comment
Это отображается, когда вы печатаете все имена полей? Может ли он быть переименован по какой-то странной причине? Почему вы используете равенство без учета регистра в фильтре имен?   -  person Stefan Haustein    schedule 18.04.2016
comment
@StefanHaustein Результат ListFields дает мне 5 дескрипторов полей — один для mMessageType и остальные 4 строковых поля mOTP1-4. члены по умолчанию в нижнем регистре, поэтому я использую сравнение без учета регистра. Также lcFieldName передается пользователем с клавиатуры.   -  person johnco3    schedule 18.04.2016
comment
Reflection::ListFields перечисляет только установленные поля, может ли это объяснить отсутствие логических полей и полей перечисления?   -  person Stefan Haustein    schedule 18.04.2016
comment
@StefanHaustein хорошее предложение, однако, к сожалению, это не так, я отредактировал вопрос, чтобы показать дополнительную распечатку и сгенерированный код, противопоставляющий поле, у которого есть дескриптор, по сравнению с этим полем mAP, которого нет. Надеюсь, это будет полезно. Спасибо   -  person johnco3    schedule 18.04.2016
comment
Вещи, которые я бы попробовал: 1. Установить поле bool локально на значение, отличное от значения по умолчанию, непосредственно перед печатью имен полей через GetReflection. 2. Перечислите имена полей, используя дескриптор сообщения вместо GetReflection.   -  person Stefan Haustein    schedule 18.04.2016
comment
@StefanHaustein отличное предложение, если я локально устанавливаю значение логического значения true в клиенте перед вызовом кода отражения (я предполагаю, что по умолчанию установлено значение false), тогда появляется свойство для этого mAP, если вместо этого я устанавливаю для него значение false ( значение по умолчанию), то дескриптор поля исчезнет. Я не уверен, что вы подразумеваете под списком имен полей с использованием дескриптора сообщения. Не могли бы вы указать мне на такой пример или написать один вкладыш? Спасибо.   -  person johnco3    schedule 18.04.2016
comment
@StefanHaustein второе предложение перечислить имена полей показывает поле независимо от того, установлено ли поле или нет. Это очень странное поведение, но я полагаю, что оно работает. Спасибо за вашу помощь   -  person johnco3    schedule 18.04.2016


Ответы (1)


Reflection::ListFields() перечислены только те поля, которые установлены в данный момент. Чтобы перебрать все поля или найти конкретное поле, используйте методы доступа к полям в Descriptor. Вы можете получить дескриптор сообщения из сообщения (или из объекта Reflection), используя GetDescriptor.

person Stefan Haustein    schedule 18.04.2016
comment
Спасибо за внимание ко всем комментариям! - person johnco3; 18.04.2016