Написание замены для Matlab C API для написания.matfiles
Я работаю над исследовательской моделью, которая выводит результаты в формат файла 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()
,
Это вызывает segfault в какой-то момент, потому что я пытаюсь получить доступ к нулевому указателю. Чтобы отследить причину, я прогнал код через 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 дочерним по отношению к другому:
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
, который затем вызывает segfault, когда я пытаюсь получить к нему доступ.
Как это может быть? оба правильно расположены при вызове mxCreateStructMatrix
Так как же эти указатели могут перекрываться? Что я пропускаю?
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
Это не очень сложно реализовать, вы просто перераспределяете массивы указателей для размещения еще одного поля и увеличения числа полей.