Issue with Protobuf Message to mxArray Converter

2 views (last 30 days)
I'm having some difficulty with a mex function that I'm writing to convert Protobuf messages in C++ to mxArrays that are then returned as Matlab structures. It seems to run ok - I was able to confirm that it runs all the way through a sample message - but it segfaults as the mex function is returning. That's made it a bit difficult for me to debug, and I suspect that I'm doing something pretty dumb (well, probably multiple things pretty dumb). I've been staring at this for days now and haven't made much progress.
My full code for the conversion function is below. I've found that I can eliminate the segfault if I modify this section:
// Iterate through each repeated message
for (int j = 0; j < count; ++j)
{
// Call this method on the sub message
const pb::Message& recurseMessage = reflection->GetRepeatedMessage(*inMsg, thisField, j);
mxSubMessage = processPbMessage(&recurseMessage);
// Itereate through and set each field
for (int subFieldIndex = 0; subFieldIndex < subFields.size(); subFieldIndex++)
{
nextField = mxGetField(mxSubMessage, 0, subFieldNames[subFieldIndex]);
mxSetField(subMexMsg, j, subFieldNames[subFieldIndex], nextField);
}
delete[] subFieldNames;
mxSetField(mexMsg, 0, fieldName.c_str(), subMexMsg);
}
If I comment out the mxSetField line, the segfault goes away (although I get no useful info out). Further, I ran a test where I only put two layers into the mxArray (so it was a structure of structures, rather than anything even deeper). Then, it was able to return, but when I tried to look at the resulting Matlab structure, I hit another segfault that referenced the function try_nonrecursive_mxGetNumberOfBytes. I suspect that this means that I'm putting data incorrectly into the mxArrays, but for the life of me I can't figure out what I'm doing wrong. I'd really really appreciate any help.
Here's the full code:
mxArray* processPbMessage(const ::google::protobuf::Message* inMsg)
{
// Set up a namespace for convenience for protobuf stuff
namespace pb = ::google::protobuf;
// Extract a descriptor and reflection for the incoming message
const pb::Descriptor* descriptor = inMsg->GetDescriptor();
const pb::Reflection* reflection = inMsg->GetReflection();
// Extract the fields from the message
vector<const pb::FieldDescriptor*> fields;
reflection->ListFields(*inMsg, &fields);
const int numFields = fields.size();
const char **field_names = new const char*[numFields];
const pb::FieldDescriptor* iField;
// Put the fields into a C style array
for (int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
{
iField = fields[fieldIndex];
field_names[fieldIndex] = iField->name().c_str();
}
// Create a Matlab structure
mwSize dims[2] = {1, 1};
mxArray *mexMsg = mxCreateStructArray(2, dims, fields.size(), field_names);
delete[] field_names;
// Initialize varaibles to hold the various value types that we might find
// Values that we extract from the protobuf messages
int intValue;
double valueDouble;
float valueFloat;
string valueString;
const pb::EnumValueDescriptor* valueEnum;
bool valueBool;
int count;
// Submessage varaibles
mxArray* mxSubMessage;
mxArray* nextField;
const pb::Descriptor* subDescriptor;
const pb::Reflection* subReflection;
vector<const pb::FieldDescriptor*> subFields;
int subNumFields;
const pb::FieldDescriptor* subIField;
int subFieldIndex;
mxArray *subMexMsg;
// Iterate through each field in the incoming message (note: this will only handle single messages)
for (int i = 0; i < fields.size(); i++)
{
// Extract the descriptor for this field
const pb::FieldDescriptor* thisField = fields[i];
const char **subFieldNames;
// Determine if this field is repeated and, if so, how many fields there are
count = 0;
if (thisField->is_repeated())
{
count = reflection->FieldSize(*inMsg, thisField);
}
else if (reflection->HasField(*inMsg, thisField))
{
count = -1;
}
else
{
mexErrMsgTxt("Field Issue!");
}
// Determine the name of the field
string fieldName = thisField->name();
// Recurse, if necessary
if (thisField->cpp_type() == pb::FieldDescriptor::CPPTYPE_MESSAGE)
{
// If it's greater than 0, we have a repeated field. Otherwise, it's singular
if (count > 0)
{
// Create a struct array for this field
const pb::Message& firstMessage = (reflection->GetRepeatedMessage(*inMsg, thisField, 0));
subDescriptor = firstMessage.GetDescriptor();
subReflection = firstMessage.GetReflection();
subReflection->ListFields(firstMessage, &subFields);
subNumFields = subFields.size();
subFieldNames = new const char*[subNumFields];
for (subFieldIndex = 0; subFieldIndex < subFields.size(); subFieldIndex++)
{
subIField = subFields[subFieldIndex];
subFieldNames[subFieldIndex] = subIField->name().c_str();
}
mwSize dims[2] = {count, 1};
subMexMsg = mxCreateStructArray(2, dims, subFields.size(), subFieldNames);
// Iterate through each repeated message
for (int j = 0; j < count; ++j)
{
// Call this method on the sub message
const pb::Message& recurseMessage = reflection->GetRepeatedMessage(*inMsg, thisField, j);
mxSubMessage = processPbMessage(&recurseMessage);
// Itereate through and set each field
for (int subFieldIndex = 0; subFieldIndex < subFields.size(); subFieldIndex++)
{
nextField = mxGetField(mxSubMessage, 0, subFieldNames[subFieldIndex]);
mxSetField(subMexMsg, j, subFieldNames[subFieldIndex], nextField);
}
delete[] subFieldNames;
mxSetField(mexMsg, 0, fieldName.c_str(), subMexMsg);
}
else
{
// Call this method on the sub-message
const pb::Message& subMessage = reflection->GetMessage(*inMsg, thisField);
mxSubMessage = processPbMessage(&subMessage);
mxSetField(mexMsg, 0, fieldName.c_str(), mxSubMessage);
}
}
else if (count <= 0)
{
// In this case, the field is not repeated and is not a message. Add it to the Matlab structure
switch(thisField->cpp_type())
{
case pb::FieldDescriptor::CPPTYPE_INT32 :
intValue = reflection->GetInt32(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateDoubleScalar((double) intValue));
break;
case pb::FieldDescriptor::CPPTYPE_INT64 :
intValue = reflection->GetInt64(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateDoubleScalar((double) intValue));
break;
case pb::FieldDescriptor::CPPTYPE_UINT32 :
intValue = reflection->GetUInt32(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateDoubleScalar((double) intValue));
break;
case pb::FieldDescriptor::CPPTYPE_UINT64 :
intValue = reflection->GetUInt64(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateDoubleScalar((double) intValue));
break;
case pb::FieldDescriptor::CPPTYPE_DOUBLE :
valueDouble = reflection->GetDouble(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateDoubleScalar(valueDouble));
break;
case pb::FieldDescriptor::CPPTYPE_FLOAT :
valueFloat = reflection->GetFloat(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateDoubleScalar((double) valueFloat));
break;
case pb::FieldDescriptor::CPPTYPE_BOOL :
valueBool = reflection->GetBool(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateLogicalScalar(valueBool));
break;
case pb::FieldDescriptor::CPPTYPE_ENUM :
valueEnum = reflection->GetEnum(*inMsg, thisField);
valueString = valueEnum->name();
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateString(valueString.c_str()));
break;
case pb::FieldDescriptor::CPPTYPE_STRING :
valueString = reflection->GetString(*inMsg, thisField);
mxSetField(mexMsg, 0, fieldName.c_str(), mxCreateString(valueString.c_str()));
break;
}
}
else{
// In this case, the field is repeated. Go through each repetition
mxArray *value;
mwSize dims2[2] = {1, count};
double *doubleArray;
mxLogical *boolArray;
mxChar* charArray;
// Initialize matrices to hold the repeated data
switch(thisField->cpp_type())
{
case pb::FieldDescriptor::CPPTYPE_INT32 :
value = mxCreateDoubleMatrix(1, count, mxREAL);
doubleArray = mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_INT64 :
value = mxCreateDoubleMatrix(1, count, mxREAL);
doubleArray = mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_UINT32 :
value = mxCreateDoubleMatrix(1, count, mxREAL);
doubleArray = mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_UINT64 :
value = mxCreateDoubleMatrix(1, count, mxREAL);
doubleArray = mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_DOUBLE :
value = mxCreateDoubleMatrix(1, count, mxREAL);
doubleArray = mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_FLOAT :
value = mxCreateDoubleMatrix(1, count, mxREAL);
doubleArray = mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_BOOL :
value = mxCreateLogicalMatrix(1, count);
boolArray = (mxLogical *) mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_STRING :
value = mxCreateCharArray(1, dims2);
charArray = (mxChar *) mxGetPr(value);
break;
case pb::FieldDescriptor::CPPTYPE_ENUM :
value = mxCreateCharArray(1, dims2);
charArray = (mxChar *) mxGetPr(value);
break;
}
for (int k = 0; k < count; ++k)
{
switch(thisField->cpp_type())
{
case pb::FieldDescriptor::CPPTYPE_INT32 :
intValue = reflection->GetRepeatedInt32(*inMsg, thisField, k);
doubleArray[k] = (double) intValue;
break;
case pb::FieldDescriptor::CPPTYPE_INT64 :
intValue = reflection->GetRepeatedInt64(*inMsg, thisField, k);
doubleArray[k] = (double) intValue;
break;
case pb::FieldDescriptor::CPPTYPE_UINT32 :
intValue = reflection->GetRepeatedUInt32(*inMsg, thisField, k);
doubleArray[k] = (double) intValue;
break;
case pb::FieldDescriptor::CPPTYPE_UINT64 :
intValue = reflection->GetRepeatedUInt64(*inMsg, thisField, k);
doubleArray[k] = (double) intValue;
break;
case pb::FieldDescriptor::CPPTYPE_DOUBLE :
valueDouble = reflection->GetRepeatedDouble(*inMsg, thisField, k);
doubleArray[k] = valueDouble;
break;
case pb::FieldDescriptor::CPPTYPE_FLOAT :
valueFloat = reflection->GetRepeatedFloat(*inMsg, thisField, k);
doubleArray[k] = (double) valueFloat;
break;
case pb::FieldDescriptor::CPPTYPE_BOOL :
valueBool = reflection->GetRepeatedBool(*inMsg, thisField, k);
boolArray[k] = valueBool;
break;
case pb::FieldDescriptor::CPPTYPE_STRING :
valueString = reflection->GetRepeatedString(*inMsg, thisField, k);
charArray[k] = (mxChar) valueString.c_str();
break;
case pb::FieldDescriptor::CPPTYPE_ENUM :
valueEnum = reflection->GetRepeatedEnum(*inMsg, thisField, k);
valueString = valueEnum->name();
charArray[k] = (mxChar) valueString.c_str();
break;
}
}
mxSetField(mexMsg, 0, fieldName.c_str(), value);
}
}
}
return mexMsg;
}
  1 Comment
Scott Lichtor
Scott Lichtor on 30 Jul 2015
Ok, I tried running a very simple example and ran into another issue that I think is very closely related (although the stack trace after the segfault is different). I created a standalone C++ file with this code:
#include <stdio.h>
#include "mex.h"
void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray
*prhs[])
{
mxSize dims[2] = {1, 1};
const char* fields1[] = {"field1", "field2"};
const char* fields2[] = {"field3"};
mxArray *mexMsg = mxCreateStructMatrix(1, 1, 2, fields1);
mxArray *subMexMsg = mxCreateStructMatrix(1, 2, 1, fields2);
int intValue = 32;
mxArray* inputMxArray = mxCreateDoubleScalar((double) intValue);
mxSetField(subMexMsg, 0, "field3", inputMxArray);
mxSetField(mexMsg, 0, "field1", subMexMsg);
mxSetField(mexMsg, 0, "field2", inputMxArray);
plhs[0] = mexMsg;
}
My goal was to create a structure of structures. The result (let’s call it res) should have looked like this res field1 subMessage field3 32 field2 32
I compiled it with Matlab using mex and ran it. Everything looked fine and as expected. Then I executed ‘clear all’ and it failed with a segfault. I hope that this should be easily repeatable on another machine. I expect that the error came about when trying to clear the mxArrays that were created in the mex function. Anyone know why this error is occurring? Thank you for any help.

Sign in to comment.

Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!