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 为 falsereal_containing_oneof()== nullptr → 外层条件 trueFieldSyntax(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