Protobuf到mxArray Routine Segfaults

时间:2015-07-21 14:36:07

标签: c++ matlab protocol-buffers

我在使用mex函数时遇到了一些困难,我正在编写将C ++中的Protobuf消息转换为mxArrays,然后将其作为Matlab结构返回。它似乎运行正常 - 我能够确认它一直在通过示例消息运行 - 但它会在mex函数返回时发生段错误。这使我调试起来有点困难,我怀疑我做了一件非常愚蠢的事(好吧,可能是多件事非常愚蠢)。我已经盯着这几个小时,并且没有取得多大进展。任何人都可以看看这个代码,看看可能会发生什么?非常感谢您的帮助:

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;

}

好的,错误可能来自这一部分:

            // 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);

            }

当我注释掉第一个mxSetField行时,段错误消失了。但是,我无法看到我这样做的方式有什么问题。我是否错误地设置了结构的结构字段?

好的,我尝试运行一个非常简单的示例并遇到另一个我认为非常密切相关的问题(尽管在segfault之后的堆栈跟踪是不同的)。我使用以下代码创建了一个独立的C ++文件:

    #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;
    }

我的目标是创建一个结构结构。结果(让我们称之为res)应该看起来像res field1 subMessage field3 32 field2 32

我用Matlab用mex编译它并运行它。一切看起来都很好,如预期的那样。然后我执行了'清除所有',它失败了一个段错误。我希望这应该可以在另一台机器上轻松重复。我希望在尝试清除在mex函数中创建的mxArrays时出现错误。有谁知道为什么会出现这个错误?谢谢你的帮助。

1 个答案:

答案 0 :(得分:0)

我要做的第一件事是获取此repo-&gt; https://github.com/kyamagu/mexplus并使用它来创建正确的数据类型。你应该做的第二件事是看看这个FileExchange条目包装C ++类 - &gt; http://www.mathworks.com/matlabcentral/fileexchange/38964-example-matlab-class-wrapper-for-a-c++-class

您正在创建的消息对象与您下次调用mex文件时访问的消息对象不同。

当然,我正在开展一些非常类似的工作,将protobuf消息带入MATLAB。如果我让我的工作和发布,我会在这里发布一个后续行动。