英文:
proto.MessageName returns empty string
问题
我在Go语言中遇到了一个关于Protobuf的问题。
给定以下代码片段:
if proto.MessageName(&messages.AddedItemEvent{}) == "" {
log.Fatal("empty")
}
这将评估为true
并退出应用程序。
什么情况下会导致protobuf在MessageName
上返回空字符串?
我可以看到类型及其名称在生成的protobuf消息代码中已注册:
func init() {
proto.RegisterType((*AddItemCommand)(nil), "messages.AddItemCommand")
proto.RegisterType((*AddedItemEvent)(nil), "messages.AddedItemEvent")
proto.RegisterType((*RenameCommand)(nil), "messages.RenameCommand")
proto.RegisterType((*RenamedEvent)(nil), "messages.RenamedEvent")
proto.RegisterType((*DumpCommand)(nil), "messages.DumpCommand")
}
我还验证了上述init
实际运行了,确实如此。
我有其他生成的proto消息,它们按预期返回它们的名称。所以这里出了什么问题?
编辑
proto文件如下所示:
syntax = "proto3";
package messages;
//user messages
message AddItemCommand {
string item = 1;
}
message AddedItemEvent {
string item = 1;
}
message RenameCommand {
string name = 1;
}
message RenamedEvent {
string name = 1;
}
message DumpCommand {}
再次编辑。
在main
函数中手动调用
proto.RegisterType((*messages.AddedItemEvent)(nil), "messages.AddedItemEvent")
可以使其正常工作。
所以在messages.init
和main
之间的某个地方清除了类型注册表。
有人知道原因吗?
英文:
I'm completely stuck on a problem with Protobuf in Go
Given the following bit of code:
if proto.MessageName(&messages.AddedItemEvent{}) == "" {
log.Fatal("empty")
}
This will evaluate to true
and exit the application.
What can possibly make protobuf return empty string on MessageName
?
I can see that the types and their names are registered as they should in the generated protobuf message code:
func init() {
proto.RegisterType((*AddItemCommand)(nil), "messages.AddItemCommand")
proto.RegisterType((*AddedItemEvent)(nil), "messages.AddedItemEvent")
proto.RegisterType((*RenameCommand)(nil), "messages.RenameCommand")
proto.RegisterType((*RenamedEvent)(nil), "messages.RenamedEvent")
proto.RegisterType((*DumpCommand)(nil), "messages.DumpCommand")
}
I have also verified that the above init
actually runs, and it does.
I have other generated proto messages, which returns their names as expected.
So what is going wrong here?
Edit
The protofile looks like this
syntax = "proto3";
package messages;
//user messages
message AddItemCommand {
string item = 1;
}
message AddedItemEvent {
string item = 1;
}
message RenameCommand {
string name = 1;
}
message RenamedEvent {
string name = 1;
}
message DumpCommand {}
Edit again.
Manually calling
proto.RegisterType((*messages.AddedItemEvent)(nil), "messages.AddedItemEvent")
From the main
func, makes it work.
So somehow the type registry is cleared somewhere between the messages.init
and main
Anyone?
答案1
得分: 1
不,类型注册表没有被清除。
很可能是因为您在生成的代码中使用了不同的proto
包实现,其中proto
包的实现选择由您选择的proto代码生成器来处理。在您的代码中,您实际上是在尝试获取MessageName
。
在Go中,每个导入的包在go二进制进程的生命周期内仅初始化一次。每个初始化的包都有自己的名称表(包括导出和未导出的名称)及其对应的值。特别是proto
包维护了所谓的“注册表”,其中包含已知的proto消息名称和类型。简单来说,注册表是一个包范围的映射,将消息类型与其对应的名称关联起来。该映射在包初始化时进行初始化和填充。然后,要检索MessageName
,您通过某个辅助函数间接访问它。关键点是,如果您在代码中使用了导入的不同proto
包实现,或者在生成的代码中使用了不同的proto
包实现,或者在依赖项中使用了不同的proto
包实现,那么很有可能在应用程序启动时,您会填充一个注册表,然后稍后,您尝试从另一个不知道它的注册表中检索MessageName
。经验法则是始终在整个应用程序中使用protoc代码生成器插件、预生成的proto类型库和来自单个供应商的proto
包实现。
最流行的proto
包(我从未使用或遇到其他包)是https://github.com/golang/protobuf和https://github.com/gogo/protobuf。
请仔细检查您的protoc生成器插件和包导入,希望一切都会好起来。
英文:
No, type registry isn't cleared.
Most probably you are using different implementations of proto
package in your generated code, where proto
package implementation choice is addressed by your proto code generator of choice. In your code you are actually trying to get MessageName
.
Each imported package in Go is initialized exactly one time during the go binary process lifetime. Each initialized package has it's own table of names (both exported and unexported) and their corresponding values. proto
package in particular maintains so called "registry" of known proto message names and types. A bit simplified, registry is a package-scoped map associating message types with their corresponding names. This map is initialized and populated on package initialization. Then, to retrieve MessageName
you access it indirectly through some helper function. The key point is, if you use different proto
package implementations imported in your code, your generated code, or in your dependencies, then chances are, on app start you populate one registry, and then later on, you are trying to retrieve MessageName
from some other registry that know nothing about it. The rule of thumb is to always use protoc code generator plugin, pregenerated proto type libraries, and proto
package implementation from a single vendor through out your app.
Most popular proto
packages (I've never used or encountered any other) are https://github.com/golang/protobuf and https://github.com/gogo/protobuf.
Double check your protoc generator plugin and package imports and I hope it'll be all fine.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论