编译协议缓冲区生成的源文件时是不是有更好的方法来解决警告?

Posted

技术标签:

【中文标题】编译协议缓冲区生成的源文件时是不是有更好的方法来解决警告?【英文标题】:Are there some better ways to address warnings when compiling protocol buffer generated source file?编译协议缓冲区生成的源文件时是否有更好的方法来解决警告? 【发布时间】:2012-11-25 05:09:31 【问题描述】:

对于一个简单的 proto 文件:

消息人 必需的 int32 id = 1; 所需的字符串名称 = 2; 可选字符串电子邮件 = 3;

它由 protoc.exe 编译,结果用于一个同样简单的测试项目,除了包含 protoc 生成的文件之外,它基本上什么都不做。

我正在使用 msvc10 构建测试项目(x64),然后它给了我很多警告:

警告 1 警告 C4244: 'return' : 从 '__int64' 转换为 'int',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\descriptor.h 1441 1 testProtobuf ... 警告 11 警告 C4267:'argument':从 'size_t' 转换为 'int',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\unknown_field_set.h 142 1 testProtobuf 警告 12 警告 C4267:'return':从 'size_t' 转换为 'int',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\unknown_field_set.h 237 1 testProtobuf ... 警告 14 警告 C4244:'=':从 '__int64' 转换为 'int',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\io\coded_stream.h 902 1 testProtobuf 警告 15 警告 C4244:'return':从 '__int64' 转换为 'int',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\io\coded_stream.h 1078 1 testProtobuf 警告 16 警告 C4267:'argument':从 'size_t' 转换为 'google::protobuf::uint32',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\wire_format_lite_inl.h 663 1 testProtobuf ... 警告 19 警告 C4267:'return':从 'size_t' 转换为 'int',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\wire_format_lite_inl.h 739 1 testProtobuf 警告 20 警告 C4267:'argument':从 'size_t' 转换为 'google::protobuf::uint32',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\wire_format_lite_inl.h 742 1 testProtobuf 警告 21 警告 C4267:'return':从 'size_t' 转换为 'int',可能丢失数据 D:\Work\protobuf-trunk\src\google\protobuf\wire_format_lite_inl.h 743 1 testProtobuf 警告 22 警告 C4267:'argument':从 'size_t' 转换为 'int',可能丢失数据 D:\Work\testProtobuf\testProtobuf\person.pb.cc 211 1 testProtobuf ... 警告 28 警告 C4996:'std::_Copy_impl':带有可能不安全参数的函数调用 - 此调用依赖于调用者检查传递的值是否正确。要禁用此警告,请使用 -D_SCL_SECURE_NO_WARNINGS。请参阅有关如何使用 Visual C++ 'Checked Iterators' C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xutility 2239 1 testProtobuf 的文档 警告 29 警告 C4996:'std::_Copy_impl':带有可能不安全参数的函数调用 - 此调用依赖于调用者检查传递的值是否正确。要禁用此警告,请使用 -D_SCL_SECURE_NO_WARNINGS。请参阅有关如何使用 Visual C++ 'Checked Iterators' C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xutility 2239 1 testProtobuf 的文档

有什么好的方法可以解决所有这些警告吗?任何建议将不胜感激。

ps。 libprotobuf 项目本身可以被 msvc10 干净编译而不会发出任何警告。

[编辑 2013/02/20]

工作解决方案:

    为那些 protoc 生成的 .cc 文件设置属性: 配置属性 -> c/c++ -> 高级 -> 禁用特定警告

【问题讨论】:

由于协议缓冲区似乎是一个开源项目,您可以修复 protoc.exe 以生成更好的代码。 如果您确定警告没有问题,您可以使用#pragma warning(disable: 4244, 4267, 4996) 将其关闭。见dr-bill.net/CSC076/class_summaries/3-26/pragmas.htm @cxxl,我认为关闭这些警告不是一个好主意,因为我自己的代码也可能存在这些问题。有没有简单的方法可以为协议缓冲区库关闭它们? 您可以在代码块的开头关闭它们并重新打开它们(在它的末尾加上#pragma warning(enable:x,x,x)。有关更多信息,请参阅我发布的链接。 @doomster,文件是protoc.exe生成的,我没有过多修改。 【参考方案1】:

一种简单的方法是使用包装头来包含生成的 protobuf 头:

#ifndef MESSAGES_WRAPPER_H
#define MESSAGES_WRAPPER_H

#ifdef _MSC_VER
  #pragma warning(push)
  #pragma warning(disable: 4018 4100 4267)
#endif

#include "messages.pb.h"

#ifdef _MSC_VER
  #pragma warning(pop)
#endif

#endif // MESSAGES_WRAPPER_H

【讨论】:

【参考方案2】:

您可以破解 protoc 编译器的源代码,让它自动将 pragma 注入到生成的文件中。

在src/google/protobuf/compiler/cpp/cpp_file.cc 中的GenerateHeader(io::Printer* printer) 第94 行附近,将第一个printer->Print 调用更改为:

  // Generate top of header.
  printer->Print(
    "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
    "// source: $filename$\n"
    "\n"
    "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n"
    "#define PROTOBUF_$filename_identifier$__INCLUDED\n"
    "\n"
    "#ifdef _MSC_VER\n"
    "#  pragma warning(push)\n"
    "#  pragma warning(disable: 4127 4244 4267)\n"
    "#endif\n"
    "\n"
    "#include <string>\n"
    "\n",
    "filename", file_->name(),
    "filename_identifier", filename_identifier);

然后在the same function at around line 294结束时,将最后一次printer-&gt;Print调用改为:

  printer->Print(
    "#ifdef _MSC_VER\n"
    "#  pragma warning(pop)\n"
    "#endif\n"
    "\n"
    "#endif  // PROTOBUF_$filename_identifier$__INCLUDED\n",
    "filename_identifier", filename_identifier);

现在您只需要编译 protoc 目标并运行新的 protoc.exe 即可在生成的标头中包含 pragma。

【讨论】:

我对在 GenerateHeaders 之后定义的 GenerateSource 做了同样的技巧。效果很好,谢谢! 这是一个非常有趣的解决方案。不过,我不介意找到一些更可重复的东西。我讨厌要求未来的开发人员在构建之前修补 protobuf。没有有效的 CMake 解决方案吗?正如 OP 所建议的那样,以某种方式仅忽略特定文件上的警告,但通过 CMake 而不是 VS 来实现?【参考方案3】:

由于维护负担,我不太喜欢破解 protoc 源,所以我想出了更简单的解决方案。

您可能已经使用某种脚本生成文件,因为您需要将许多参数传递给 protoc,因此进一步自定义此脚本非常容易。

我创建了对生成的文件进行后处理的 powershell 脚本,并在生成文件的开头添加了#include "Grpc.Header.h",并在所述文件的末尾添加了#include "Grpc.Footer.h"。然后,您可以在放置在项目中时自定义这些头文件。除了使用#pragma warning 禁用警告之外,我还可以在需要时轻松引入其他更改。

注意:在此示例中,我生成了我的项目所需的额外 #define,以及对 MSVC 很好的预编译头支持。我将其留在示例中以说明您可以做什么,但可以安全地删除它。 dllexport_decl=MYAPI 允许在您的 dll 项目中导出 protobuf 类。

GenerateProto.ps1

$ProjectDirectory = "My\Project\Directory\"
$ProtoDirectory = "My\Proto\Directory"

protoc --proto_path=$ProtoDirectory --cpp_out=dllexport_decl=MYAPI:$ProjectDirectory universe.proto

$Plugin = where.exe grpc_cpp_plugin

protoc --proto_path=$ProtoDirectory --grpc_out=$ProjectDirectory --plugin=protoc-gen-grpc=$Plugin universe.proto

foreach($File in Get-ChildItem $ProjectDirectory* -Include *.pb.cc, *.pb.h )

    $Content = Get-Content $File

    if( $File.FullName.EndsWith(".cc"))
    
        Set-Content $File "#include `"pch.h`""
        Add-Content $File "#include `"Grpc.Header.h`""
    
    else
    
        Set-Content $File "#include `"Grpc.Header.h`""
    

    Add-Content $File $Content
    Add-Content $File "#include `"Grpc.Footer.h`""

Grpc.Header.h

#pragma warning(push)
#pragma warning(disable: 4251)

#ifdef API_EXPORT
#  define MYAPI __declspec(dllexport)
#else
#  define MYAPI __declspec(dllimport)
#endif

Grpc.Footer.h

#pragma warning(pop)

【讨论】:

【参考方案4】:

编译器给你这些警告是正确的,因为存在截断的风险。

如果转换是安全的,请使用显式转换。这比盲目禁用警告更干净。

【讨论】:

代码是从protoc自动生成的。这不是他写的。如果他要更改自动生成的headers/translation units 中的代码,那么每次他进行更改并需要重新编译.proto 文件时都必须这样做......

以上是关于编译协议缓冲区生成的源文件时是不是有更好的方法来解决警告?的主要内容,如果未能解决你的问题,请参考以下文章

协议缓冲区比序列化更好?

如何将已编译的协议缓冲区转换回 .proto 文件?

协议缓冲区(Protocol Buffers)

在 mac os 上执行程序时 libprotobuf 检查失败

您如何管理协议缓冲区定义文件?

在协议缓冲区中注释推送 rpc 调用