protoc-gen-c 支持 proto3 `optional` 关键字修改记录

protoc-gen-c 支持 proto3 optional 关键字修改记录

https://github.com/protobuf-c/protobuf-c/releases/tag/v1.5.2

1. 背景

protobuf-c 1.5.2 原版不支持 proto3 的 optional 关键字。当 protoc 处理包含 optional 关键字的 proto3 文件时,会输出以下错误:

复制代码
Proto_SendStepInfo.proto: is a proto3 file that contains optional fields,
but code generator protoc-gen-c hasn't been updated to support optional
fields in proto3. Please ask the owner of this code generator to support
proto3 optional.

该错误并非由 protoc-gen-c 本身产生,而是由 protoc 在调用代码生成器插件之前进行的特性检查 所触发。protoc 通过查询插件返回的 GetSupportedFeatures() 来判断插件是否支持 FEATURE_PROTO3_OPTIONAL


2. 根因分析

2.1 特性声明缺失

protoc-gen-c 的代码生成器类 CGenerator 继承自 google::protobuf::compiler::CodeGenerator,实现了 GetSupportedFeatures() 方法。原代码返回 0,意味着插件声明不支持任何扩展特性,包括 proto3 optional。

复制代码
// 修改前 (c_generator.h 第 97 行)
uint64_t GetSupportedFeatures() const { return 0; }

2.2 proto3 optional 字段的标签错误

在 proto3 中,带有 optional 关键字的字段会被 protobuf 内部包装到一个合成 oneof (synthetic oneof)中。protoc-gen-c 在生成 descriptor 初始化代码时,对 proto3 可选字段的标签处理逻辑错误------将其当作普通 proto3 字段处理,设置 LABEL = "NONE",导致运行时无法正确跟踪字段 presence。

复制代码
// 修改前 (c_field.cc 第 127-133 行)
// proto3 optional 字段进入了此分支
if (descriptor_->containing_oneof() != NULL &&
    descriptor_->real_containing_oneof() == NULL) {
  variables["LABEL"] = "NONE";          // <-- 错误!应该为 OPTIONAL
}

3. 修改内容

3.1 修改 1:声明支持 FEATURE_PROTO3_OPTIONAL

文件protoc-gen-c/c_generator.h(第 97 行)

复制代码
// 修改前
uint64_t GetSupportedFeatures() const { return 0; }

// 修改后
uint64_t GetSupportedFeatures() const { return FEATURE_PROTO3_OPTIONAL; }

作用 :告诉 protoc(protobuf 36.x)当前插件支持 proto3 optional 字段。当 FEATURE_PROTO3_OPTIONAL 标志被设置时,protoc 不会拒绝包含 optional 关键字的 proto3 文件,而是将字段传递给插件处理。

原理 :在 protobuf 36.x 中,CodeGenerator::GetSupportedFeatures() 的返回值通过插件协议(plugin.proto)传递给 protoc 的命令行接口。protoc 在 command_line_interface.cc 第 2752 行检查:

cpp 复制代码
supported_features & CodeGenerator::FEATURE_PROTO3_OPTIONAL;

只有该标志位被设置时,protoc 才允许 proto3 的 optional 字段传入代码生成器。

3.2 修改 2:修正 descriptor label 为 OPTIONAL

文件protoc-gen-c/c_field.cc(第 127-133 行)

复制代码
// 修改前
if (descriptor_->containing_oneof() != NULL &&
    descriptor_->real_containing_oneof() == NULL) {
  variables["LABEL"] = "NONE";
} else {
  variables["LABEL"] = "NONE";
  optional_uses_has = false;
}

// 修改后
if (descriptor_->containing_oneof() != NULL &&
    descriptor_->real_containing_oneof() == NULL) {
  variables["LABEL"] = CamelToUpper(GetLabelName(descriptor_));  // → "OPTIONAL"
} else {
  variables["LABEL"] = "NONE";
  optional_uses_has = false;
}

作用 :对于 proto3 optional 字段(处于合成 oneof 中,containing_oneof() != NULL && real_containing_oneof() == NULL),将 descriptor 中的 label 从 PROTOBUF_C_LABEL_NONE 修正为 PROTOBUF_C_LABEL_OPTIONAL

原理GetLabelName() 对非 required、非 repeated 的字段返回 "optional"CamelToUpper() 将其转为 "OPTIONAL",最终 descriptor 中生成 PROTOBUF_C_LABEL_OPTIONAL,使运行时链路(protobuf-c.c)能正确识别和处理该字段的 presence 标志位。


4. 未修改代码说明(已有正确逻辑)

以下代码未作修改,但理解它们对验证修改正确性至关重要。

4.1 结构体成员生成(has_xxx 字段)

proto3 optional 字段的 has_xxx 标志位原本就能正确生成。以 c_primitive_field.cc(第 114-120 行)为例:

cpp 复制代码
// c_primitive_field.cc 第 114-120 行(原型,其他 field generator 同理)
if (descriptor_->containing_oneof() == NULL ||
    descriptor_->real_containing_oneof() == nullptr) {
  if (FieldSyntax(descriptor_) == 2 ||
      (descriptor_->containing_oneof() != NULL &&
       descriptor_->real_containing_oneof() == nullptr))
    printer->Print(vars, "protobuf_c_boolean has_$name$$deprecated$;\n");
}

对于 proto3 optional 字段:

  • containing_oneof() != NULL → 条件 1 为 false
  • real_containing_oneof() == nullptr → 外层条件 true
  • FieldSyntax(descriptor_) == 2 → false(proto3)
  • containing_oneof() != NULL && real_containing_oneof() == nullptr → true → 生成 has_xxx

该逻辑在所有字段类型生成器中一致:

文件 行号 模式
c_primitive_field.cc 114-120 相同
c_message_field.cc 88-95 相同
c_bytes_field.cc 99-106 相同
c_string_field.cc 101-108 相同
c_enum_field.cc 100-106 containing_oneof() == NULL 条件略不同,但结果一致

4.2 合成 oneof 跳过逻辑

c_message.cc(第 197-204 行)中生成结构体成员时,通过 real_containing_oneof() 跳过合成 oneof 中的字段:

cpp 复制代码
// c_message.cc 第 195-204 行
for (int i = 0; i < descriptor_->field_count(); i++) {
  const google::protobuf::FieldDescriptor* field = descriptor_->field(i);
  if (field->real_containing_oneof() == NULL) {  // ← 合成 oneof 的字段不会跳过
    // ... 生成 struct 成员
    field_generators_.get(field).GenerateStructMembers(printer);
  }
}

proto3 optional 字段的 real_containing_oneof() 返回 NULL(因为合成 oneof 不是 "real" 的),所以这些字段会正常进入结构体成员生成流程------这是正确的行为。

4.3 真实 oneof 联合体生成(跳过合成 oneof)

c_message.cc(第 207-208 行)在生成 oneof 联合体时,只处理真实 oneof:

cpp 复制代码
// c_message.cc 第 207-208 行
// Generate unions from real oneofs (skip synthetic proto3 optional oneofs)
for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) {

real_oneof_decl_count() 不计入合成 oneof,因此 proto3 optional 字段不会生成多余的 union 成员------这也是正确的。

4.4 字段名生成规则

c_helpers.cc(第 292-298 行):

cpp 复制代码
std::string FieldName(const google::protobuf::FieldDescriptor* field) {
  std::string result = ToLower(field->name());  // 全部转小写
  if (kKeywords.count(result) > 0) {
    result.append("_");                          // C 关键字加 _ 后缀
  }
  return result;
}

该规则对 proto2 和 proto3 字段一致,无需修改。


5. 完整 diff 汇总

5.1 c_generator.h

diff 复制代码
--- a/protoc-gen-c/c_generator.h
+++ b/protoc-gen-c/c_generator.h
@@ -94,7 +94,7 @@ class PROTOBUF_C_EXPORT CGenerator : public google::protobuf::compiler::CodeGene

 #if GOOGLE_PROTOBUF_VERSION >= 5026000
-  uint64_t GetSupportedFeatures() const { return 0; }
+  uint64_t GetSupportedFeatures() const { return FEATURE_PROTO3_OPTIONAL; }
   google::protobuf::Edition GetMinimumEdition() const { return google::protobuf::Edition::EDITION_PROTO2; }
   google::protobuf::Edition GetMaximumEdition() const { return google::protobuf::Edition::EDITION_PROTO3; }
 #endif

5.2 c_field.cc

diff 复制代码
--- a/protoc-gen-c/c_field.cc
+++ b/protoc-gen-c/c_field.cc
@@ -126,7 +126,7 @@ void FieldGenerator::GenerateDescriptorInitializerGeneric(...) const
   if (FieldSyntax(descriptor_) == 3 && !descriptor_->is_required() && !descriptor_->is_repeated()) {
     if (descriptor_->containing_oneof() != NULL &&
         descriptor_->real_containing_oneof() == NULL) {
-      variables["LABEL"] = "NONE";
+      variables["LABEL"] = CamelToUpper(GetLabelName(descriptor_));
     } else {
       variables["LABEL"] = "NONE";
       optional_uses_has = false;

6. 验证结果

6.1 测试用 proto 文件

使用 Proto_SendStepInfo.proto(proto3,43 个字段,其中 38 个 optional):

protobuf 复制代码
syntax = "proto3";
package MiddleProto;

message Proto_SendStepInfo {
    int32 ProtoVer = 1;
    int64 CmdSeqNo = 2;
    bytes CCUGUID = 3;
    bytes MapGUID = 4;
    bytes StartGUID = 5;

    optional int32 GlobalTimeRecord = 6;
    optional int32 GlobalVoltageRecord = 7;
    // ...(共 38 个 optional 字段)
    optional int32 GlobalProtLessSOC = 43;
}

6.2 生成代码验证

生成的头文件 Proto_SendStepInfo.pb-c.h 关键结构:

c 复制代码
struct MiddleProto__ProtoSendStepInfo {
    ProtobufCMessage base;

    // proto3 non-optional 字段(无 has_xxx)
    int32_t protover;
    int64_t cmdseqno;
    ProtobufCBinaryData ccuguid;
    ProtobufCBinaryData mapguid;
    ProtobufCBinaryData startguid;

    // proto3 optional 字段(有 has_xxx)
    protobuf_c_boolean has_globaltimerecord;
    int32_t globaltimerecord;
    protobuf_c_boolean has_globalvoltagerecord;
    int32_t globalvoltagerecord;
    // ...(共 38 对)
    protobuf_c_boolean has_globalprotlesssoc;
    int32_t globalprotlesssoc;
};

Descriptor 初始化代码的关键行:

c 复制代码
{
    "globaltimerecord",
    6,
    PROTOBUF_C_LABEL_OPTIONAL,    // ← 正确
    PROTOBUF_C_TYPE_INT32,
    offsetof(MiddleProto__ProtoSendStepInfo, has_globaltimerecord),  // ← 正确
    offsetof(MiddleProto__ProtoSendStepInfo, globaltimerecord),
    NULL, NULL, 0, 0, NULL, NULL
},

对比 non-optional 字段:

c 复制代码
{
    "protover",
    1,
    PROTOBUF_C_LABEL_NONE,        // ← 正确(proto3 non-optional)
    PROTOBUF_C_TYPE_INT32,
    0,                            // ← 无 has_xxx 偏移
    offsetof(MiddleProto__ProtoSendStepInfo, protover),
    NULL, NULL, 0, 0, NULL, NULL
},

6.3 运行时测试结果

6 组测试,42/42 全部通过

测试 验证内容 结果
Test 1 proto3 non-optional 字段 + optional 默认 has=0 10/10 PASS
Test 2 设置 5 个 optional 字段 → has=1,其余 has=0 10/10 PASS
Test 3 所有 38 个 optional 未设置 → has 全为 0 4/4 PASS
Test 4 Proto_SendStepInfoResp 消息 7/7 PASS
Test 5 optional 边界值(0、大正数、大负数) 9/9 PASS
Test 6 空缓冲区反序列化返回空消息 2/2 PASS

7. 边界测试结果汇总

在验证 proto3 optional 修改之前,已对 protobuf-c 的 proto2 功能进行了完整的边界条件测试(83/83 通过),覆盖 38 个 optional 字段的序列化/反序列化,包括:

  • 所有标量类型(double/float/int32/int64/uint32/uint64/sint32/sint64/fixed32/fixed64/sfixed32/sfixed64/bool/string/bytes)
  • 整数边界值(INT32_MAX、INT32_MIN、UINT32_MAX)
  • 浮点特殊值(NaN、Infinity、π)
  • 零值测试(所有数值类型 = 0)
  • 空字符串、空 bytes
  • 含 \0 的二进制数据
  • 长字符串(2047 字符)
  • 大二进制数据(8KB)
  • 100 元素 repeated 字段
  • 嵌套消息
  • 枚举边界值
  • 5 轮重复序列化/反序列化
  • 空缓冲区反序列化
  • 默认值覆盖

8. 涉及文件清单

文件 修改/涉及 说明
protoc-gen-c/c_generator.h 修改 GetSupportedFeatures() 返回 FEATURE_PROTO3_OPTIONAL
protoc-gen-c/c_field.cc 修改 proto3 optional 的 LABEL 从 NONE 改为 OPTIONAL
protoc-gen-c/c_primitive_field.cc 未修改(涉及) 生成基础类型的 has_xxx 成员
protoc-gen-c/c_message_field.cc 未修改(涉及) 生成消息类型的 has_xxx 成员
protoc-gen-c/c_bytes_field.cc 未修改(涉及) 生成 bytes 类型的 has_xxx 成员
protoc-gen-c/c_string_field.cc 未修改(涉及) 生成 string 类型的 has_xxx 成员
protoc-gen-c/c_enum_field.cc 未修改(涉及) 生成枚举类型的 has_xxx 成员
protoc-gen-c/c_message.cc 未修改(涉及) 合成 oneof 跳过逻辑、真实 oneof 联合体生成
protoc-gen-c/c_helpers.cc 未修改(涉及) FieldName()GetLabelName()

9. 构建方式

使用 MSVC(VS2022)编译:

powershell 复制代码
$cl = "D:\Program Files (x86)\vs2022\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\cl.exe"
$link = "D:\Program Files (x86)\vs2022\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\link.exe"
$srcDir = "e:\Work\Temp\protobuf-c-1.5.2"
$pbDir = "E:\Work\Temp\protobuf-main"

$cflags = @(
    "/nologo", "/O2", "/MT", "/EHsc", "/std:c++17",
    "/DPACKAGE_VERSION=1.5.2",
    "/I$srcDir", "/I$srcDir\protobuf-c", "/I$srcDir\protoc-gen-c\build",
    "/I$pbDir\src", "/I$pbDir\..\abseil-cpp", "/I$pbDir\third_party\utf8_range"
)

# 1. 编译 protobuf-c 运行时库
& $cl $cflags /Fo"$outDir\protobuf-c.obj" /c "$srcDir\protobuf-c\protobuf-c.c"
& $link /lib /out:"$outDir\protobuf-c.lib" "$outDir\protobuf-c.obj"

# 2. 编译 protoc-gen-c 插件
$sources = Get-ChildItem "$srcDir\protoc-gen-c\*.cc" | % { $_.FullName }
$sources += "$srcDir\protoc-gen-c\build\protobuf-c\protobuf-c.pb.cc"
foreach ($src in $sources) {
    & $cl $cflags /Fo"$outDir\$(Split-Path $src -LeafBase).obj" /c $src
}
& $link /out:"$outDir\protoc-gen-c.exe" @objs @abslLibs libprotobuf.lib libprotoc.lib

10. 使用说明

10.1 代码生成

powershell 复制代码
protoc --c_out=OUTPUT_DIR ^
       --plugin=protoc-gen-c=PATH\protoc-gen-c.exe ^
       -I PROTO_DIR ^
       PROTO_FILE.proto

10.2 编译生成的 C 代码

powershell 复制代码
cl /I include\protobuf-c /MT /std:c11 *.pb-c.c protobuf-c.lib

10.3 proto3 与 proto2 的差异

特性 proto3 (non-optional) proto2 / proto3 (optional)
默认值 类型零值 proto 中声明的默认值
has_xxx 标志
LABEL PROTOBUF_C_LABEL_NONE PROTOBUF_C_LABEL_OPTIONAL
零值序列化 不序列化 序列化(因为有 has_xxx=1)
c 复制代码
## 附录 A:修改后的完整源码

### A.1 `protoc-gen-c/c_generator.h`

```cpp
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

// Copyright (c) 2008-2025, Dave Benson and the protobuf-c authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Modified to implement C code by Dave Benson.

// Generates C code for a given .proto file.

#ifndef PROTOBUF_C_PROTOC_GEN_C_C_GENERATOR_H__
#define PROTOBUF_C_PROTOC_GEN_C_C_GENERATOR_H__

#include <string>

#include <google/protobuf/compiler/code_generator.h>

#if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB)
# define PROTOBUF_C_EXPORT __declspec(dllexport)
#else
# define PROTOBUF_C_EXPORT
#endif

namespace protobuf_c {

// CodeGenerator implementation which generates a C++ source file and
// header.  If you create your own protocol compiler binary and you want
// it to support C++ output, you can do so by registering an instance of this
// CodeGenerator with the CommandLineInterface in your main() function.
class PROTOBUF_C_EXPORT CGenerator : public google::protobuf::compiler::CodeGenerator {
 public:
  CGenerator();
  ~CGenerator();

  // implements CodeGenerator ----------------------------------------
  bool Generate(const google::protobuf::FileDescriptor* file,
                const std::string& parameter,
                google::protobuf::compiler::OutputDirectory* output_directory,
                std::string* error) const;

#if GOOGLE_PROTOBUF_VERSION >= 5026000
  uint64_t GetSupportedFeatures() const { return FEATURE_PROTO3_OPTIONAL; }
  google::protobuf::Edition GetMinimumEdition() const { return google::protobuf::Edition::EDITION_PROTO2; }
  google::protobuf::Edition GetMaximumEdition() const { return google::protobuf::Edition::EDITION_PROTO3; }
#endif
};

}  // namespace protobuf_c

#endif  // PROTOBUF_C_PROTOC_GEN_C_C_GENERATOR_H__

A.2 protoc-gen-c/c_field.cc

cpp 复制代码
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

// Copyright (c) 2008-2025, Dave Benson and the protobuf-c authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Modified to implement C code by Dave Benson.

#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/common.h>

#include <protobuf-c/protobuf-c.pb.h>

#include "c_bytes_field.h"
#include "c_enum_field.h"
#include "c_field.h"
#include "c_helpers.h"
#include "c_message_field.h"
#include "c_primitive_field.h"
#include "c_string_field.h"
#include "compat.h"

namespace protobuf_c {

FieldGenerator::~FieldGenerator()
{
}

static bool is_packable_type(google::protobuf::FieldDescriptor::Type type)
{
  return type == google::protobuf::FieldDescriptor::TYPE_DOUBLE
      || type == google::protobuf::FieldDescriptor::TYPE_FLOAT
      || type == google::protobuf::FieldDescriptor::TYPE_INT64
      || type == google::protobuf::FieldDescriptor::TYPE_UINT64
      || type == google::protobuf::FieldDescriptor::TYPE_INT32
      || type == google::protobuf::FieldDescriptor::TYPE_FIXED64
      || type == google::protobuf::FieldDescriptor::TYPE_FIXED32
      || type == google::protobuf::FieldDescriptor::TYPE_BOOL
      || type == google::protobuf::FieldDescriptor::TYPE_UINT32
      || type == google::protobuf::FieldDescriptor::TYPE_ENUM
      || type == google::protobuf::FieldDescriptor::TYPE_SFIXED32
      || type == google::protobuf::FieldDescriptor::TYPE_SFIXED64
      || type == google::protobuf::FieldDescriptor::TYPE_SINT32
      || type == google::protobuf::FieldDescriptor::TYPE_SINT64;
    //TYPE_BYTES
    //TYPE_STRING
    //TYPE_GROUP
    //TYPE_MESSAGE
}

void FieldGenerator::GenerateDescriptorInitializerGeneric(google::protobuf::io::Printer* printer,
							  bool optional_uses_has,
							  const std::string &type_macro,
							  const std::string &descriptor_addr) const
{
  std::map<std::string, std::string> variables;
  const google::protobuf::OneofDescriptor *oneof = descriptor_->real_containing_oneof();
  const ProtobufCFileOptions opt = descriptor_->file()->options().GetExtension(pb_c_file);
  variables["TYPE"] = type_macro;
  variables["classname"] = FullNameToC(FieldScope(descriptor_)->full_name(), FieldScope(descriptor_)->file());
  variables["name"] = FieldName(descriptor_);
  if (opt.use_oneof_field_name())
    variables["proto_name"] = std::string(oneof->name());
  else
    variables["proto_name"] = std::string(descriptor_->name());
  variables["descriptor_addr"] = descriptor_addr;
  variables["value"] = SimpleItoa(descriptor_->number());
  if (oneof != NULL)
    variables["oneofname"] = CamelToLower(oneof->name());

  if (FieldSyntax(descriptor_) == 3 && !descriptor_->is_required() && !descriptor_->is_repeated()) {
    if (descriptor_->containing_oneof() != NULL &&
        descriptor_->real_containing_oneof() == NULL) {
      variables["LABEL"] = CamelToUpper(GetLabelName(descriptor_));
    } else {
      variables["LABEL"] = "NONE";
      optional_uses_has = false;
    }
  } else {
    variables["LABEL"] = CamelToUpper(GetLabelName(descriptor_));
  }

  if (descriptor_->has_default_value()) {
    variables["default_value"] = std::string("&")
                               + FullNameToLower(descriptor_->full_name(), descriptor_->file())
			       + "__default_value";
  } else if (FieldSyntax(descriptor_) == 3 &&
    descriptor_->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {
    variables["default_value"] = "&protobuf_c_empty_string";
  } else {
    variables["default_value"] = "NULL";
  }

  variables["flags"] = "0";

  if (descriptor_->is_repeated()
   && is_packable_type (descriptor_->type())
   && descriptor_->options().packed()) {
    variables["flags"] += " | PROTOBUF_C_FIELD_FLAG_PACKED";
  } else if (descriptor_->is_repeated()
   && is_packable_type (descriptor_->type())
   && FieldSyntax(descriptor_) == 3
   && !descriptor_->options().has_packed()) {
    variables["flags"] += " | PROTOBUF_C_FIELD_FLAG_PACKED";
  }

  if (descriptor_->options().deprecated())
    variables["flags"] += " | PROTOBUF_C_FIELD_FLAG_DEPRECATED";

  if (oneof != NULL)
    variables["flags"] += " | PROTOBUF_C_FIELD_FLAG_ONEOF";

  // Eliminate codesmell "or with 0"
  if (variables["flags"].find("0 | ") == 0) {
   variables["flags"].erase(0, 4);
  }

  printer->Print("{\n");
  if (descriptor_->file()->options().has_optimize_for() &&
        descriptor_->file()->options().optimize_for() ==
        google::protobuf::FileOptions_OptimizeMode_CODE_SIZE) {
     printer->Print("  NULL, /* CODE_SIZE */\n");
  } else {
     printer->Print(variables, "  \"$proto_name$\",\n");
  }
  printer->Print(variables,
    "  $value$,\n"
    "  PROTOBUF_C_LABEL_$LABEL$,\n"
    "  PROTOBUF_C_TYPE_$TYPE$,\n");
  if (descriptor_->is_required()) {
    printer->Print(variables, "  0,   /* quantifier_offset */\n");
  } else if (!descriptor_->is_required() && !descriptor_->is_repeated()) {
    if (oneof != NULL) {
      printer->Print(variables, "  offsetof($classname$, $oneofname$_case),\n");
    } else if (optional_uses_has) {
      printer->Print(variables, "  offsetof($classname$, has_$name$),\n");
    } else {
      printer->Print(variables, "  0,   /* quantifier_offset */\n");
    }
  } else if (descriptor_->is_repeated()) {
    printer->Print(variables, "  offsetof($classname$, n_$name$),\n");
  }
  printer->Print(variables, "  offsetof($classname$, $name$),\n");
  printer->Print(variables, "  $descriptor_addr$,\n");
  printer->Print(variables, "  $default_value$,\n");
  printer->Print(variables, "  $flags$,             /* flags */\n");
  printer->Print(variables, "  0,NULL,NULL    /* reserved1,reserved2, etc */\n");
  printer->Print("},\n");
}

FieldGeneratorMap::FieldGeneratorMap(const google::protobuf::Descriptor* descriptor)
  : descriptor_(descriptor),
    field_generators_(
      new std::unique_ptr<FieldGenerator>[descriptor->field_count()]) {
  // Construct all the FieldGenerators.
  for (int i = 0; i < descriptor->field_count(); i++) {
    field_generators_[i].reset(MakeGenerator(descriptor->field(i)));
  }
}

FieldGenerator* FieldGeneratorMap::MakeGenerator(const google::protobuf::FieldDescriptor* field) {
  const ProtobufCFieldOptions opt = field->options().GetExtension(pb_c_field);
  switch (field->type()) {
    case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
      return new MessageFieldGenerator(field);
    case google::protobuf::FieldDescriptor::TYPE_STRING:
      if (opt.string_as_bytes())
        return new BytesFieldGenerator(field);
      else
        return new StringFieldGenerator(field);
    case google::protobuf::FieldDescriptor::TYPE_BYTES:
      return new BytesFieldGenerator(field);
    case google::protobuf::FieldDescriptor::TYPE_ENUM:
      return new EnumFieldGenerator(field);
    case google::protobuf::FieldDescriptor::TYPE_GROUP:
      return 0;			// XXX
    default:
      return new PrimitiveFieldGenerator(field);
  }
}

FieldGeneratorMap::~FieldGeneratorMap() {}

const FieldGenerator& FieldGeneratorMap::get(
    const google::protobuf::FieldDescriptor* field) const {
  GOOGLE_CHECK_EQ(field->containing_type(), descriptor_);
  return *field_generators_[field->index()];
}

}  // namespace protobuf_c
复制代码
相关推荐
aaaameliaaa2 小时前
指针之总结
c语言·笔记·算法
agathakuan2 小时前
Wireshark 解密並導出TLS 1.2 / TLS 1.3 明文的方法(可控制 Client 端)
c语言·wireshark·ssl·openwrt
noipp3 小时前
推荐题目:洛谷 P6231 [JSOI2013] 公交系统
c语言·数据结构·c++·算法·游戏·洛谷·luogu
憧憬成为java架构高手的小白3 小时前
黑马八股--中间件学习之MQ(RabbitMQ)
学习·中间件
c238563 小时前
C/C++每日一练6
c语言·c++·算法
wdfk_prog3 小时前
嵌入式面试真题第 13 题:单硬件 Timer 下的高精度多路软件定时器架构设计
c语言·开发语言·面试·职场和发展·嵌入式
时间的拾荒人4 小时前
MySQL C语言连接 - 从入门到实战
android·c语言·mysql
撩得Android一次心动4 小时前
Linux编程笔记4【个人用】
linux·笔记·学习
user-猴子5 小时前
让网页“活”过来 —— 用AI打造会自主学习的动态知识图谱
人工智能·学习·知识图谱