The second code you suggested is not safe and should not be used, as it could crash MATLAB. Instead you should write:
mxArray *arr = mxCreateCellMatrix(len, 1);
mxArray *str = mxCreateString("Hello");
for(mwIndex i=0; i<len; i++) {
mxSetCell(arr, i, mxDuplicateArray(str));
}
mxDestroyArray(str);
plhs[0] = arr;
This is unfortunately not the most efficient use of memory storage. Imagine that instead of using a tiny string, we were storing a very large matrix (duplicated along the cells).
Now it is possible to do what you initially wanted, but you'll have to be resort to undocumented hacks (like creating shared data copies or manually increment the reference count in the mxArray_tag
structure).
In fact this is what usually happens behind the scenes in MATLAB. Take this for example:
>> c = cell(100,100);
>> c(:) = {rand(5000)};
As you know a cell array in MATLAB is basically an mxArray
whose data-pointer points to an array of other mxArray
variables.
In the case above, MATLAB first creates an mxArray
corresponding to the 5000x5000 matrix. This will be stored in the first cell c{1}
.
For the rest of the cells, MATLAB creates "lightweight" mxArray
s, that basically share its data with the first cell element, i.e its data pointer points to the same block of memory holding the huge matrix.
So there is only one copy of the matrix at all times, unless of course you modify one of them (c{2,2}(1)=99
), at which point MATLAB has to "unlink" the array and make a separate copy for this cell element.
You see internally each mxArray
structure has a reference counter and a cross-link pointer to make this data sharing possible.
Hint: You can study this data sharing behavior with format debug
option turned on, and comparing the pr
pointer address of the various
cells.
The same concept holds true for structure fields, so when we write:
x = rand(5000);
s = struct('a',x, 'b',x, 'c',x);
all the fields would point to the same copy of data in x
..
EDIT:
I forgot to show the undocumented solution I mentioned :)
mex_test.cpp
#include "mex.h"
extern "C" mxArray* mxCreateReference(mxArray*);
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mwSize len = 10;
mxArray *arr = mxCreateCellMatrix(len, 1);
mxArray *str = mxCreateString("Hello");
for(mwIndex i=0; i<len; i++) {
// I simply replaced the call to mxDuplicateArray here
mxSetCell(arr, i, mxCreateReference(str));
}
mxDestroyArray(str);
plhs[0] = arr;
}
MATLAB
>> %c = repmat({'Hello'}, 10, 1);
>> c = mex_test()
>> c{1} = 'bye'
>> clear c
The mxCreateReference
function will increment the internal reference counter of the str
array each time it is called, thus letting MATLAB know that there are other copies of it.
So when you clear the resulting cell arrays, it will in turn decrement this counter one for each cell, until the counter reaches 0 at which point it is safe to destroy the array in question.
Using the array directly (mxSetCell(arr, i, str)
) is problematic because the ref-counter immediately reaches zero after destroying the first cell. Thus for subsequent cells, MATLAB will attempt to free arrays that have already been freed, resulting in memory corruption.