具有重复字段的嵌套 protobuf 结构导致调试断言失败
Nested protobuf structure with repeated field leads to debug assertion failure
见更新 2 的最小示例。
我正在尝试使用 protobuf 和 TCP/IP 将数据从一个进程发送到另一个进程。为此,我创建了以下原型文件:
syntax = "proto3";
option cc_enable_arenas = false;
message TCPMessage {
enum Type {
SETUP = 0;
DATA = 1;
START = 2;
STOP = 3;
}
Type messageType = 1;
oneof message {
SetupMessage setupMessage = 2;
DataMessage dataMessage = 3;
StartMessage startMessage = 4;
StopMessage stopMessage = 5;
}
uint64 timestamp = 6;
}
message StartMessage{
bool diagnosticMode = 1;
}
message StopMessage{
}
message SetupMessage {
repeated string entities = 1;
repeated string objects = 2;
repeated string commands = 3;
repeated VariableDescription commandDescriptions = 4;
repeated ProtoVariable initialState = 5;
}
message DataMessage {
repeated ProtoVariable variables = 1;
uint64 timeSpan = 2;
}
message VariableDescription {
enum DataType {
DOUBLE = 0;
// FLOAT = 1;
// INT32 = 2;
INT64 = 3;
// UINT32 = 4;
// UINT64 = 5;
// Reserved if ever needed
// SINT32 = 6;
// SINT64 = 7;
// FIXED32 = 8;
// FIXED64 = 9;
// SFIXED32 = 10;
// SFIXED64 = 11;
BOOL = 12;
STRING = 13;
BYTES = 14;
}
string entity = 1;
string name = 2;
DataType dataType = 3;
repeated uint64 dimensions = 4;
}
message ProtoVariable
{
VariableDescription metaData = 1;
bytes data = 2;
}
如您所见,我使用了一个带有重复字段的嵌套消息结构,将某些变量的信息从一个进程发送到另一个进程。在 python 端(接收端)我的代码没有问题。所有信息都按预期收到,一切正常。然而,在 C++ 方面,我正在 运行 解决由删除 TCPMessage 对象引起的调试断言问题。
在消息字段中发送填充有 StartMessage 的 TCPMessage 时,我没有遇到任何问题,一切都按预期进行,但是,在消息字段中发送 DataMessage 时,我 运行 遇到了所描述的问题。
首先是代码,我是如何创建 StartMessage 的:
start_msg.set_messagetype(TCPMessage_Type_START);
StartMessage* tmp = new StartMessage();
start_msg.set_allocated_startmessage(tmp);
sendMessage(std::make_unique<TCPMessage>(start_msg))
正如我所说,这行得通。在网上阅读了 mutable_foo()
方法后,我创建了以下用于创建 DataMessage 的代码:
std::unique_ptr<TCPMessage> msg = std::make_unique<TCPMessage>();
msg->set_messagetype(TCPMessage::DATA);
DataMessage* data_msg = msg->mutable_datamessage();
auto var = data_msg->add_variables();
VariableDescription* meta_data = var->mutable_metadata();
meta_data->set_entity(entity);
meta_data->set_name(cmd_identifier);
meta_data->set_datatype(stored_meta_data.getType());
for (uint64_t i = 0; i < stored_meta_data.getDimensions().size(); i++) {
meta_data->add_dimensions(stored_meta_data.getDimensions()[i]);
}
double val = 12345.6789;
char const* d = reinterpret_cast<char const*>(&val);
std::string* data_str = var->mutable_data();
for (int i = 0; i < 8; ++i) {
data_str->operator+=(d[i]);
}
sendMessage(std::move(msg));
我知道有一些奇怪的代码(参见 data_str->operator+=(d[i])
例如)这与我试图以任何可能的方式让它工作有关。
最后是sendMessage(std::unique_ptr<TCPMessage> msg)
方法的代码:
int TCPConnection::sendMessage(std::unique_ptr<TCPMessage>& msg)
{
// bool a = msg->messagetype() == TCPMessage::DATA; // This was used for debugging (see further down)
std::string out = "";
msg->SerializeToString(&out);
std::string message_len = "";
for (int i = 0; i < 8; ++i) {
message_len += char((int)(((uint64_t)out.size() >> (i * 8)) & 0xFF));
}
std::string out_buffer = "";
size_t i = 0;
for (; i < 8; ++i) {
out_buffer += message_len[i];
}
size_t j = 0;
for (; j < out.size(); ++j) {
out_buffer += out[j];
}
int i_send_result = ::send(tcp_client_socket_, &out_buffer[0], out.size() + 8, 0);
if (i_send_result == SOCKET_ERROR) {
std::cout << "send failed with error: " << WSAGetLastError() << std::endl;
closesocket(tcp_client_socket_);
WSACleanup();
return i_send_result;
}
// if (a) {
// int o = 1;
// msg->~TCPMessage(); // Here I was figuring out, that the debug assertion happens in ~DataMessage()
// in the RepeatedPtrField<Element>::~RepeatedPtrField() destructor
// }
return i_send_result;
}
如果有人能指出我在内存分配或其他任何地方出错的地方,我将非常高兴!我尝试使用 set_allocated_datamessage()
函数或 set_data()
函数或或多或少我能想到的...
其他可能感兴趣的事情:
- 消息创建和消息发送分两次进行
不同的线程。我使用线程安全(我希望)队列来获取
unique_ptr 从一个线程到另一个线程。
- 我尝试在 arena 中创建消息,我可以在 arena 上调用
arena.Reset()
而不会出错,所以我想消息的销毁通常有效(?)
- 如果我删除消息创建和
发送,所以我想堆分配问题确实在那里。
- 我可以毫无问题地发送不同的消息,一开始是
StartMessage
发送时不会抛出调试断言错误或
相似。
VisualStudio 2019调试模式运行时触发的断点指向VisualStudio MSVC文件夹内delete_scalar.cpp文件中的以下代码(如果有帮助):
_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block) noexcept
{
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK); //<-- Breakpoint
#else
free(block);
#endif
}
这是调用堆栈:
更新:
我设法发现,这显然是由于 VariableDescription
消息 name
和 entity
的 string
字段造成的。为了找出问题所在,我将生成 TCP 消息的代码更改为以下内容:
TCPMessage* msg = google::protobuf::Arena::CreateMessage<TCPMessage>(proto_arena_);
msg->set_messagetype(TCPMessage::DATA);
DataMessage* data_msg = google::protobuf::Arena::CreateMessage<DataMessage>(proto_arena_);
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
string test = "test";
md->set_allocated_name(&test); // I tried this
// md->set_name("test") // And this
// auto n = md->mutable_name();// And those two lines
// n->assign("test");
msg->set_allocated_datamessage(data_msg);
auto msg_string = msg->SerializeAsString(); // If I remove this it runs through?!
proto_arena_->Reset();
所有设置名称的尝试均无效。但是,如果我使用
auto n = md->mutable_name();
n->push_back('a');
有效!但是,如果我逐个遍历字符串和 push_back 每个字符,它就不起作用...
更新 2:
我只是尝试了我能想到的最简单的事情,我仍然有同样的问题。我更改了原型文件中的一些内容,所以这里是一个完整的示例:
main.cpp:
#include <iostream>
#include "tcp_data_message.pb.h"
int main()
{
auto msg_t = std::make_unique<TCPMessage>();
msg_t->set_messagetype(TCPMessage_Type_DATA);
DataMessage* data_msg = msg_t->mutable_datamessage();
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
md->set_entityid(1);
md->set_id(2345678);
}
原型文件:
syntax = "proto3";
option cc_enable_arenas = true;
option optimize_for = LITE_RUNTIME;
message TCPMessage {
enum Type {
SETUP = 0;
DATA = 1;
START = 2;
STOP = 3;
// DATATEST = 4;
}
Type messageType = 1;
oneof message {
SetupMessage setupMessage = 2;
DataMessage dataMessage = 3;
StartMessage startMessage = 4;
StopMessage stopMessage = 5;
// DataMessageTest dataMessagetest = 7;
}
uint64 timestamp = 6;
}
message StartMessage{
bool diagnosticMode = 1;
}
message StopMessage{
}
message SetupMessage {
map<string, int32> entities = 1;
map<string, int32> objects = 2;
map<string, int32> commands = 3;
repeated CommandDescription commandDescriptions = 4;
}
message CommandDescription {
VariableDescription description = 1;
string name = 2;
}
message DataMessage {
repeated ProtoVariable variables = 1;
uint64 timeSpan = 2;
}
message VariableDescription {
enum DataType {
DOUBLE = 0;
// FLOAT = 1;
// INT32 = 2;
INT64 = 3;
// UINT32 = 4;
// UINT64 = 5;
// Reserved if ever needed
// SINT32 = 6;
// SINT64 = 7;
// FIXED32 = 8;
// FIXED64 = 9;
// SFIXED32 = 10;
// SFIXED64 = 11;
BOOL = 12;
STRING = 13;
BYTES = 14;
}
int32 entityID = 1;
int32 ID = 2;
DataType dataType = 3;
repeated uint64 dimensions = 4;
}
message ProtoVariable
{
VariableDescription metaData = 1;
bytes data = 2;
}
message VariableDescriptionOld {
enum DataType {
DOUBLE = 0;
// FLOAT = 1;
// INT32 = 2;
INT64 = 3;
// UINT32 = 4;
// UINT64 = 5;
// Reserved if ever needed
// SINT32 = 6;
// SINT64 = 7;
// FIXED32 = 8;
// FIXED64 = 9;
// SFIXED32 = 10;
// SFIXED64 = 11;
BOOL = 12;
STRING = 13;
BYTES = 14;
}
string entity = 1;
string name = 2;
DataType dataType = 3;
repeated uint64 dimensions = 4;
}
message ProtoVariableOld
{
VariableDescriptionOld metaData = 1;
bytes data = 2;
}
我仍然遇到同样的错误...
但是,如果我创建一个 SetupMessage,它会起作用:
#include <iostream>
#include "tcp_data_message.pb.h"
int main()
{
auto msg_t = std::make_unique<TCPMessage>();
msg_t->set_messagetype(TCPMessage_Type_SETUP);
auto setup_msg = msg_t->mutable_setupmessage();
auto entities = setup_msg->mutable_entities();
(*entities)["test"] = 123;
/*
DataMessage* data_msg = msg_t->mutable_datamessage();
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
md->set_entityid(1);
md->set_id(2345678);
*/
}
更新 3:
显然是 libprotobuf-lite.dll 和 libprotobuf-lited.dll 的问题,我没有为我的代码使用调试 dll。我现在设法得到了我的最小示例 运行ning,但是我 运行 遇到了另一个问题。将消息从字符串解析为消息时,出现读取访问冲突错误。但是,在我的最小示例中它有效...
最小示例:
#include <iostream>
#include "tcp_data_message.pb.h"
int main()
{
std::string in = "18 97 10 12 10 4 98 97 108 108 16 -1 -1 -1 -1 7 18 14 10 10 112 111 115 105 116 105 111 110 95 121 16 1 18 14 10 10 118 101 108 111 99 105 116 121 95 121 16 2 26 18 10 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 16 3 34 29 10 11 8 -1 -1 -1 -1 7 16 3 34 1 1 18 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 0";
//^this is the chars I'm trying to parse
std::string s = "";
size_t pos = 0;
std::string delimiter = " ";
while ((pos = in.find(delimiter)) != std::string::npos) {
std::string token = in.substr(0, pos);
char c = static_cast<char>(std::stoi(token));
s += c;
in.erase(0, pos + delimiter.length());
}
std::cout << s << std::endl;
{
auto msg_t = std::make_unique<TCPMessage>();
if (msg_t->ParseFromString(s)) {
std::cout << "Wuhu!" << std::endl;
}
msg_t->set_messagetype(TCPMessage_Type_SETUP);
auto setup_msg = msg_t->mutable_setupmessage();
auto entities = setup_msg->mutable_entities();
(*entities)["test"] = 123;
DataMessage* data_msg = msg_t->mutable_datamessage();
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
md->set_entityid(1);
md->set_id(2345678);
}
std::cout << "Test" << std::endl;
}
如果我复制粘贴整个字符串并创建它等等,我的“正确”代码中会出现读取访问冲突。我相信这一定是dll或类似问题。
“真实”代码:
//...
std::string in = "18 97 10 12 10 4 98 97 108 108 16 -1 -1 -1 -1 7 18 14 10 10 112 111 115 105 116 105 111 110 95 121 16 1 18 14 10 10 118 101 108 111 99 105 116 121 95 121 16 2 26 18 10 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 16 3 34 29 10 11 8 -1 -1 -1 -1 7 16 3 34 1 1 18 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 0";
std::string s = "";
size_t pos = 0;
std::string delimiter = " ";
while ((pos = in.find(delimiter)) != std::string::npos) {
std::string token = in.substr(0, pos);
char c = static_cast<char>(std::stoi(token));
s += c;
in.erase(0, pos + delimiter.length());
}
//std::cout << "Received Message: " << buf_string << std::endl;
TCPMessage* msg = new TCPMessage();
if (!msg->ParseFromString(s)) {
std::cout << "ERROR: Parsing Message from String failed" << std::endl;
return NULL;
}
当我通过 VisualStudio 调试代码时,我发现 ParseFromString(std::string data)
中的数据对象已经显示“无法读取内存”。抛出的异常是:
Exception thrown at 0x01133B87 (vcruntime140d.dll) in program.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
它向我展示了 memcpy.asm 的这一部分:
任何我可能出错的想法。
这是调试器在调用'msg->ParseFromString(s)'之前显示给我的内容:
这是它在 ParseFromString(ConstStringParam data)
方法中向我展示的内容:
我终于弄清楚问题出在哪里了,所以对于遇到类似问题的每个人:
1 对于第一个描述的堆问题,请确保您使用正确的库进行适当的配置(调试/发布)。我使用 libprotobuf.lib/dll 而不是 libprotobufd.lib/dll.
2 对于传递给 ParseFromString()
方法的字符串的问题:我在程序的预处理器定义中设置了 _ITERATOR_DEBUG_LEVEL=0
。这与 vcpkg 生成的 dll 不兼容。为了使其兼容,您必须将 _ITERATOR_DEBUG_LEVEL=0
包含到 protobuflib 的编译中。您可以通过添加
set(VCPKG_C_FLAGS_DEBUG "/D_ITERATOR_DEBUG_LEVEL=0")
set(VCPKG_CXX_FLAGS_DEBUG "/D_ITERATOR_DEBUG_LEVEL=0")
到vcpkg中用于编译proto库的cmake文件。例如。 C:\Program Files\vcpkg\triplets\x86-windows.cmake
如果调用 vcpkg install protobuf protobuf:x86-windows
(最好创建一个新的 cmake 文件并在安装命令中的冒号后调用它)。然后在您的项目中使用新创建的 lib/dll 个文件。
见更新 2 的最小示例。
我正在尝试使用 protobuf 和 TCP/IP 将数据从一个进程发送到另一个进程。为此,我创建了以下原型文件:
syntax = "proto3";
option cc_enable_arenas = false;
message TCPMessage {
enum Type {
SETUP = 0;
DATA = 1;
START = 2;
STOP = 3;
}
Type messageType = 1;
oneof message {
SetupMessage setupMessage = 2;
DataMessage dataMessage = 3;
StartMessage startMessage = 4;
StopMessage stopMessage = 5;
}
uint64 timestamp = 6;
}
message StartMessage{
bool diagnosticMode = 1;
}
message StopMessage{
}
message SetupMessage {
repeated string entities = 1;
repeated string objects = 2;
repeated string commands = 3;
repeated VariableDescription commandDescriptions = 4;
repeated ProtoVariable initialState = 5;
}
message DataMessage {
repeated ProtoVariable variables = 1;
uint64 timeSpan = 2;
}
message VariableDescription {
enum DataType {
DOUBLE = 0;
// FLOAT = 1;
// INT32 = 2;
INT64 = 3;
// UINT32 = 4;
// UINT64 = 5;
// Reserved if ever needed
// SINT32 = 6;
// SINT64 = 7;
// FIXED32 = 8;
// FIXED64 = 9;
// SFIXED32 = 10;
// SFIXED64 = 11;
BOOL = 12;
STRING = 13;
BYTES = 14;
}
string entity = 1;
string name = 2;
DataType dataType = 3;
repeated uint64 dimensions = 4;
}
message ProtoVariable
{
VariableDescription metaData = 1;
bytes data = 2;
}
如您所见,我使用了一个带有重复字段的嵌套消息结构,将某些变量的信息从一个进程发送到另一个进程。在 python 端(接收端)我的代码没有问题。所有信息都按预期收到,一切正常。然而,在 C++ 方面,我正在 运行 解决由删除 TCPMessage 对象引起的调试断言问题。
在消息字段中发送填充有 StartMessage 的 TCPMessage 时,我没有遇到任何问题,一切都按预期进行,但是,在消息字段中发送 DataMessage 时,我 运行 遇到了所描述的问题。
首先是代码,我是如何创建 StartMessage 的:
start_msg.set_messagetype(TCPMessage_Type_START);
StartMessage* tmp = new StartMessage();
start_msg.set_allocated_startmessage(tmp);
sendMessage(std::make_unique<TCPMessage>(start_msg))
正如我所说,这行得通。在网上阅读了 mutable_foo()
方法后,我创建了以下用于创建 DataMessage 的代码:
std::unique_ptr<TCPMessage> msg = std::make_unique<TCPMessage>();
msg->set_messagetype(TCPMessage::DATA);
DataMessage* data_msg = msg->mutable_datamessage();
auto var = data_msg->add_variables();
VariableDescription* meta_data = var->mutable_metadata();
meta_data->set_entity(entity);
meta_data->set_name(cmd_identifier);
meta_data->set_datatype(stored_meta_data.getType());
for (uint64_t i = 0; i < stored_meta_data.getDimensions().size(); i++) {
meta_data->add_dimensions(stored_meta_data.getDimensions()[i]);
}
double val = 12345.6789;
char const* d = reinterpret_cast<char const*>(&val);
std::string* data_str = var->mutable_data();
for (int i = 0; i < 8; ++i) {
data_str->operator+=(d[i]);
}
sendMessage(std::move(msg));
我知道有一些奇怪的代码(参见 data_str->operator+=(d[i])
例如)这与我试图以任何可能的方式让它工作有关。
最后是sendMessage(std::unique_ptr<TCPMessage> msg)
方法的代码:
int TCPConnection::sendMessage(std::unique_ptr<TCPMessage>& msg)
{
// bool a = msg->messagetype() == TCPMessage::DATA; // This was used for debugging (see further down)
std::string out = "";
msg->SerializeToString(&out);
std::string message_len = "";
for (int i = 0; i < 8; ++i) {
message_len += char((int)(((uint64_t)out.size() >> (i * 8)) & 0xFF));
}
std::string out_buffer = "";
size_t i = 0;
for (; i < 8; ++i) {
out_buffer += message_len[i];
}
size_t j = 0;
for (; j < out.size(); ++j) {
out_buffer += out[j];
}
int i_send_result = ::send(tcp_client_socket_, &out_buffer[0], out.size() + 8, 0);
if (i_send_result == SOCKET_ERROR) {
std::cout << "send failed with error: " << WSAGetLastError() << std::endl;
closesocket(tcp_client_socket_);
WSACleanup();
return i_send_result;
}
// if (a) {
// int o = 1;
// msg->~TCPMessage(); // Here I was figuring out, that the debug assertion happens in ~DataMessage()
// in the RepeatedPtrField<Element>::~RepeatedPtrField() destructor
// }
return i_send_result;
}
如果有人能指出我在内存分配或其他任何地方出错的地方,我将非常高兴!我尝试使用 set_allocated_datamessage()
函数或 set_data()
函数或或多或少我能想到的...
其他可能感兴趣的事情:
- 消息创建和消息发送分两次进行 不同的线程。我使用线程安全(我希望)队列来获取 unique_ptr 从一个线程到另一个线程。
- 我尝试在 arena 中创建消息,我可以在 arena 上调用
arena.Reset()
而不会出错,所以我想消息的销毁通常有效(?) - 如果我删除消息创建和 发送,所以我想堆分配问题确实在那里。
- 我可以毫无问题地发送不同的消息,一开始是
StartMessage
发送时不会抛出调试断言错误或 相似。
VisualStudio 2019调试模式运行时触发的断点指向VisualStudio MSVC文件夹内delete_scalar.cpp文件中的以下代码(如果有帮助):
_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block) noexcept
{
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK); //<-- Breakpoint
#else
free(block);
#endif
}
这是调用堆栈:
更新:
我设法发现,这显然是由于 VariableDescription
消息 name
和 entity
的 string
字段造成的。为了找出问题所在,我将生成 TCP 消息的代码更改为以下内容:
TCPMessage* msg = google::protobuf::Arena::CreateMessage<TCPMessage>(proto_arena_);
msg->set_messagetype(TCPMessage::DATA);
DataMessage* data_msg = google::protobuf::Arena::CreateMessage<DataMessage>(proto_arena_);
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
string test = "test";
md->set_allocated_name(&test); // I tried this
// md->set_name("test") // And this
// auto n = md->mutable_name();// And those two lines
// n->assign("test");
msg->set_allocated_datamessage(data_msg);
auto msg_string = msg->SerializeAsString(); // If I remove this it runs through?!
proto_arena_->Reset();
所有设置名称的尝试均无效。但是,如果我使用
auto n = md->mutable_name();
n->push_back('a');
有效!但是,如果我逐个遍历字符串和 push_back 每个字符,它就不起作用...
更新 2:
我只是尝试了我能想到的最简单的事情,我仍然有同样的问题。我更改了原型文件中的一些内容,所以这里是一个完整的示例:
main.cpp:
#include <iostream>
#include "tcp_data_message.pb.h"
int main()
{
auto msg_t = std::make_unique<TCPMessage>();
msg_t->set_messagetype(TCPMessage_Type_DATA);
DataMessage* data_msg = msg_t->mutable_datamessage();
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
md->set_entityid(1);
md->set_id(2345678);
}
原型文件:
syntax = "proto3";
option cc_enable_arenas = true;
option optimize_for = LITE_RUNTIME;
message TCPMessage {
enum Type {
SETUP = 0;
DATA = 1;
START = 2;
STOP = 3;
// DATATEST = 4;
}
Type messageType = 1;
oneof message {
SetupMessage setupMessage = 2;
DataMessage dataMessage = 3;
StartMessage startMessage = 4;
StopMessage stopMessage = 5;
// DataMessageTest dataMessagetest = 7;
}
uint64 timestamp = 6;
}
message StartMessage{
bool diagnosticMode = 1;
}
message StopMessage{
}
message SetupMessage {
map<string, int32> entities = 1;
map<string, int32> objects = 2;
map<string, int32> commands = 3;
repeated CommandDescription commandDescriptions = 4;
}
message CommandDescription {
VariableDescription description = 1;
string name = 2;
}
message DataMessage {
repeated ProtoVariable variables = 1;
uint64 timeSpan = 2;
}
message VariableDescription {
enum DataType {
DOUBLE = 0;
// FLOAT = 1;
// INT32 = 2;
INT64 = 3;
// UINT32 = 4;
// UINT64 = 5;
// Reserved if ever needed
// SINT32 = 6;
// SINT64 = 7;
// FIXED32 = 8;
// FIXED64 = 9;
// SFIXED32 = 10;
// SFIXED64 = 11;
BOOL = 12;
STRING = 13;
BYTES = 14;
}
int32 entityID = 1;
int32 ID = 2;
DataType dataType = 3;
repeated uint64 dimensions = 4;
}
message ProtoVariable
{
VariableDescription metaData = 1;
bytes data = 2;
}
message VariableDescriptionOld {
enum DataType {
DOUBLE = 0;
// FLOAT = 1;
// INT32 = 2;
INT64 = 3;
// UINT32 = 4;
// UINT64 = 5;
// Reserved if ever needed
// SINT32 = 6;
// SINT64 = 7;
// FIXED32 = 8;
// FIXED64 = 9;
// SFIXED32 = 10;
// SFIXED64 = 11;
BOOL = 12;
STRING = 13;
BYTES = 14;
}
string entity = 1;
string name = 2;
DataType dataType = 3;
repeated uint64 dimensions = 4;
}
message ProtoVariableOld
{
VariableDescriptionOld metaData = 1;
bytes data = 2;
}
我仍然遇到同样的错误...
但是,如果我创建一个 SetupMessage,它会起作用:
#include <iostream>
#include "tcp_data_message.pb.h"
int main()
{
auto msg_t = std::make_unique<TCPMessage>();
msg_t->set_messagetype(TCPMessage_Type_SETUP);
auto setup_msg = msg_t->mutable_setupmessage();
auto entities = setup_msg->mutable_entities();
(*entities)["test"] = 123;
/*
DataMessage* data_msg = msg_t->mutable_datamessage();
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
md->set_entityid(1);
md->set_id(2345678);
*/
}
更新 3:
显然是 libprotobuf-lite.dll 和 libprotobuf-lited.dll 的问题,我没有为我的代码使用调试 dll。我现在设法得到了我的最小示例 运行ning,但是我 运行 遇到了另一个问题。将消息从字符串解析为消息时,出现读取访问冲突错误。但是,在我的最小示例中它有效...
最小示例:
#include <iostream>
#include "tcp_data_message.pb.h"
int main()
{
std::string in = "18 97 10 12 10 4 98 97 108 108 16 -1 -1 -1 -1 7 18 14 10 10 112 111 115 105 116 105 111 110 95 121 16 1 18 14 10 10 118 101 108 111 99 105 116 121 95 121 16 2 26 18 10 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 16 3 34 29 10 11 8 -1 -1 -1 -1 7 16 3 34 1 1 18 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 0";
//^this is the chars I'm trying to parse
std::string s = "";
size_t pos = 0;
std::string delimiter = " ";
while ((pos = in.find(delimiter)) != std::string::npos) {
std::string token = in.substr(0, pos);
char c = static_cast<char>(std::stoi(token));
s += c;
in.erase(0, pos + delimiter.length());
}
std::cout << s << std::endl;
{
auto msg_t = std::make_unique<TCPMessage>();
if (msg_t->ParseFromString(s)) {
std::cout << "Wuhu!" << std::endl;
}
msg_t->set_messagetype(TCPMessage_Type_SETUP);
auto setup_msg = msg_t->mutable_setupmessage();
auto entities = setup_msg->mutable_entities();
(*entities)["test"] = 123;
DataMessage* data_msg = msg_t->mutable_datamessage();
auto v = data_msg->add_variables();
auto md = v->mutable_metadata();
md->set_datatype(VariableDescription_DataType_DOUBLE);
auto dim = md->mutable_dimensions();
dim->Add(1);
md->set_entityid(1);
md->set_id(2345678);
}
std::cout << "Test" << std::endl;
}
如果我复制粘贴整个字符串并创建它等等,我的“正确”代码中会出现读取访问冲突。我相信这一定是dll或类似问题。
“真实”代码:
//...
std::string in = "18 97 10 12 10 4 98 97 108 108 16 -1 -1 -1 -1 7 18 14 10 10 112 111 115 105 116 105 111 110 95 121 16 1 18 14 10 10 118 101 108 111 99 105 116 121 95 121 16 2 26 18 10 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 16 3 34 29 10 11 8 -1 -1 -1 -1 7 16 3 34 1 1 18 14 115 101 116 95 118 101 108 111 99 105 116 121 95 121 0";
std::string s = "";
size_t pos = 0;
std::string delimiter = " ";
while ((pos = in.find(delimiter)) != std::string::npos) {
std::string token = in.substr(0, pos);
char c = static_cast<char>(std::stoi(token));
s += c;
in.erase(0, pos + delimiter.length());
}
//std::cout << "Received Message: " << buf_string << std::endl;
TCPMessage* msg = new TCPMessage();
if (!msg->ParseFromString(s)) {
std::cout << "ERROR: Parsing Message from String failed" << std::endl;
return NULL;
}
当我通过 VisualStudio 调试代码时,我发现 ParseFromString(std::string data)
中的数据对象已经显示“无法读取内存”。抛出的异常是:
Exception thrown at 0x01133B87 (vcruntime140d.dll) in program.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
它向我展示了 memcpy.asm 的这一部分:
任何我可能出错的想法。
这是调试器在调用'msg->ParseFromString(s)'之前显示给我的内容:
这是它在 ParseFromString(ConstStringParam data)
方法中向我展示的内容:
我终于弄清楚问题出在哪里了,所以对于遇到类似问题的每个人:
1 对于第一个描述的堆问题,请确保您使用正确的库进行适当的配置(调试/发布)。我使用 libprotobuf.lib/dll 而不是 libprotobufd.lib/dll.
2 对于传递给 ParseFromString()
方法的字符串的问题:我在程序的预处理器定义中设置了 _ITERATOR_DEBUG_LEVEL=0
。这与 vcpkg 生成的 dll 不兼容。为了使其兼容,您必须将 _ITERATOR_DEBUG_LEVEL=0
包含到 protobuflib 的编译中。您可以通过添加
set(VCPKG_C_FLAGS_DEBUG "/D_ITERATOR_DEBUG_LEVEL=0")
set(VCPKG_CXX_FLAGS_DEBUG "/D_ITERATOR_DEBUG_LEVEL=0")
到vcpkg中用于编译proto库的cmake文件。例如。 C:\Program Files\vcpkg\triplets\x86-windows.cmake
如果调用 vcpkg install protobuf protobuf:x86-windows
(最好创建一个新的 cmake 文件并在安装命令中的冒号后调用它)。然后在您的项目中使用新创建的 lib/dll 个文件。