【问题标题】:Writing a replacement for the Matlab C API for writing .matfiles为编写 .matfiles 的 Matlab C API 编写替代品
【发布时间】:2011-11-10 18:00:53
【问题描述】:

我正在研究模型,该模型将结果输出为 matlab 的 .mat 文件格式,并最初与 matlab 库链接以使用其文件输出功能。 最近,需求发生了变化(谁会猜到),以前只支持 linux 的代码现在必须在 windows 上编译,最好不需要 matlab 来构建 - 但仍然输出 .mat 文件。
所以我搜索并找到了libmatio(http://sourceforge.net/projects/matio/)。虽然这很容易在 linux 中链接(您只需从存储库安装它),但它在 windows 上很糟糕(基本上没有关于在 windows 上构建它的信息)。事实上,似乎 Windows 支持实际上在 1.3.3 版(早在 2008 年)中被默默地删除了。
此外,API 与 matlab 提供的完全不同,这需要我重写/重组大量代码的方式。

所以我想出了这个疯狂的想法...... 我需要一个替代 Matlab API 的插件,最好不使用库(以便非程序员可以轻松编译),所以我开始编写一个。
我只是在实现我需要的功能(编写双精度数组、字符串和复杂双精度数组,以及结构和结构嵌套)。所有这些都可以正常工作,除了一个:结构数组。

所有 matlab 数据都包含在一个名为“mxArray”的结构中,根据它的类型,它包含指向双精度、复双精度或一个或多个其他 mxArray 的指针。
将 mxArray 写入文件之前的最后一步是以字节为单位计算其大小(及其子项的大小),方法是调用 calcArraySize()
这在某些时候会导致段错误,因为我正在尝试访问空指针。为了追查原因,我通过 valgrind 运行了代码。与往常一样,我会尝试按出现的顺序处理任何问题,因为它们可能是后来发生的事情的原因。
所以 valgrind 告诉我的第一件事是:

==8405== Invalid write of size 8
==8405==    at 0x00404541: mxSetFieldByNumber (mxSetFieldByNumber.c:18) [A]
==8405==    by 0x00411679: calcAllRayInfo (calcAllRayInfo.c:156)
==8405==    by 0x0041dd42: main (cTraceo.c:111)
==8405==    Address 0x5500250 is 0 bytes inside a block of size 4 alloc'd
==8405==    at 0x04c28f9f: malloc (vg_replace_malloc.c:236)
==8405==    by 0x00401066: mallocChar (toolsMemory.c:69)
==8405==    by 0x00404314: mxCreateStructMatrix (mxCreateStructMatrix.c:43) [B]
==8405==    by 0x00411235: calcAllRayInfo (calcAllRayInfo.c:105)
==8405==    by 0x0041dd42: main (cTraceo.c:111)

注意:我在下面的代码中标记了 [A] 和 [B]。
结构定义(仅显示相关成员):

struct mxArray{
  bool           isStruct;  //determines if this mxArray is a structure (which contains other mxArrays)
  bool           isChild;   //determines wheter this mxArray is a Child of another (when set, its name will not be written to the matfile, as it is already defined in the parent's fieldnames
  uintptr_t      nFields;
  char           **fieldNames;  //something like: {"theta","r","z"};
  struct mxArray **field; //pointer to member mxArrays. only used when isStruct is set.
};
typedef struct mxArray mxArray;

我用来为 structMatrix 及其内容分配内存的函数:

mxArray* mxCreateStructMatrix(uintptr_t nRows, uintptr_t nCols, uintptr_t nFields, const char **fieldNames){
  /*
   * creates a 2D array of structures
   */
  mxArray*  outArray = NULL;

  /* do some input value validation */

  // allocate memory
  outArray  = malloc(nRows*nCols*sizeof(mxArray));
  if (outArray == NULL){
    fatal("mxCreateStructMatrix(): memory allocation error.");
  }

  // allocate memory for structure members (fields)
  for (uintptr_t iStruct=0; iStruct<nCols*nRows; iStruct++){
    outArray[iStruct].nFields       = nFields;
    outArray[iStruct].fieldNames        = malloc(nFields*sizeof(char*));

    //copy fieldnames into struct info
    for (uintptr_t iField=0; iField<nFields; iField++){
      //NOTE: strlen returns length of string not including the terminating NULL character
      outArray[iStruct].fieldNames[iField] = mallocChar(strlen(fieldNames[iField])+1);  // [B] <=======
      strncpy(outArray[iStruct].fieldNames[iField], fieldNames[iField], strlen(fieldNames[iField]));
    }

    outArray[iStruct].field     = NULL;
    outArray[iStruct].field     = malloc(nFields*sizeof(mxArray*));
    if (outArray[iStruct].field == NULL){
      fatal("mxCreateStructMatrix(): memory allocation error.\n");
    }
  }
return outArray;
}

mxArrays 存在另外两个分配函数:

mxArray* mxCreateDoubleMatrix(uintptr_t nRows, uintptr_t nCols, uintptr_t numericType){
  /*
   * creates a 2D array of double precision floating point values.
   * can be real or complex.
   */
  [snip]
}
mxArray* mxCreateString(const char *inString)
  /*
   * creates an mxArray containing a string.
   */
  [snip]
}

此函数将一个 mxArray 指定为另一个 mxArray 的子级:

void    mxSetFieldByNumber(mxArray* mxStruct,       //pointer to the mxStruct
                           uint32_t index,      //linear index of the element 
                           uint32_t iField,     //index of the structure's field which we want to set.
                           mxArray* inArray){       //the mxArray we want to assign to the mxStruct
  /* 
   * Assigns an mxArray to one of the fields of a structArray
   */
  inArray->isChild = true;  //determines that this mxArray is a child of another one
  mxStruct[index].field[iField] = inArray;  // [A] <===============
}

用法是:

//create parent mxArray:
mxStruct = mxCreateStructMatrix(1, //number of rows
                                1, //number of columns
                                2, //number of fields in each element
                                fieldNames1);   //list of field names

//create children:
mxY = mxCreateDoubleMatrix(1 ,1, mxREAL);
mxZ = mxCreateDoubleMatrix(1 ,1, mxREAL);
mxSubStruct = mxCreateStructMatrix(1, //number of rows
                                   1, //number of columns
                                   3, //number of fields in each element
                                   fieldNames2); //list of field names

/* copy some values into the mxArrays */
[snip]

//link children to parents
mxSetFieldByNumber( mxStruct, //pointer to the parent mxArray
                    0,        //index of the element (linear)
                    0,        //position of the field (in this case, field 0 is "w"
                    mxY);     //the mxArray we want to add to the mxStruct

mxSetFieldByNumber( mxStruct,   0,  1,  mxZ);

mxSetFieldByNumber( mxSubStruct,    0,  0,  mxY);
mxSetFieldByNumber( mxSubStruct,    0,  1,  mxZ);

mxSetFieldByNumber( mxStruct,   0,  2,  mxSubStruct);

显然,mxStruct[index].field[iField] = inArray; 正在写入mxStruct[index].fieldNames,从而留下mxStruct[index].field[iField] == NULL,然后当我尝试访问它时会导致段错误。
怎么会这样?调用mxCreateStructMatrix 时两者都正确分配,那么这些指针如何重叠?我忽略了什么?

【问题讨论】:

  • 有没有机会将 C++ 与干净的类、std::vectors 等一起使用?
  • 你要写什么版本的.mat文件?较新版本的 Matlab 使用 hdf5 格式,因此您可以只使用 hdf5 库并向其中添加 Matlab 特定信息。
  • @Gabriel:我从未使用过 C++,只使用过 C。
  • @Jonas:我正在编写第 6 版 matfile。我还检查了 hdf5,但重点是不必更改我现有的代码库,它已经使用 Matlab API 来编写文件。这就是为什么我要编写与 API 中的函数名称相同的函数。
  • @EmanuelEy:也许我误解了你的问题,但是 matfile 类型不会只依赖于文件写入 API 吗?换句话说,您可以创建更简单的 v7 代码,而不是使用或反向工程 Matlab API 来编写 v6 mat 文件?再说一次,我可能误解了你,我不知道你的所有限制,但如果你以后无论如何都无法使用 Matlab API,那么编写 v7 可能值得考虑。

标签: c matlab c99


【解决方案1】:

我认为问题在于你的最后一句话:

mxSetFieldByNumber( mxStruct,   0,  /* THIRD FIELD */ 3,  mxSubStruct);

您正试图将mxStruct 的第三个字段分配给另一个嵌套结构变量,问题是mxStruct 只定义了两个字段:

mxStruct = mxCreateStructMatrix(1, 1, /* TWO */ 2, fieldNames1);

与 MATLAB 不同,您的代码(据我所知)不支持动态添加结构字段:

%# -- MATLAB code --
s = struct('f1',[], 'f2',[]);
s.f3 = 99;       %# add a new field

这不会很难实现,您只需重新分配指针数组以容纳更多字段并增加字段计数。

【讨论】:

  • 实际上您正在尝试访问结构的第 4 个字段(C 中从零开始的索引)
  • 您是对的,但该错误源自我为帖子所做的代码简化。在它失败的情况下,我实际上有一个包含 12 个字段的结构,但这只是在这里发布的巨大。我已经编辑了帖子以更正索引。
  • 关于动态添加结构字段:这是一个有用的功能,也许我稍后会实现它-谢谢!
  • 所以,虽然你的答案不太正确,但它让我再次检查了我的代码,结果是:我正在写入结构数组中不存在的位置 - 所以很简单......但是,嘿,这也意味着我的代码实际上正在工作:) 只需要在输入验证中再添加一个条件。
  • 我的这个小项目已经每天都在使用一段时间了,所以我把它上传到github
猜你喜欢
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 2017-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多