Get a subset of a structure array in mex
1 view (last 30 days)
Show older comments
Is it possible to get a subset of a structure array in mex? In other words, the mex equivalent of the following in Matlab:
s2 = s1(2:5); %s1 is a structure array
In my (current) use case, the structure array is empty, although fields have been defined.
3 Comments
Accepted Answer
Jim Hokanson
on 19 Dec 2016
Edited: Jim Hokanson
on 19 Dec 2016
1 Comment
James Tursa
on 19 Dec 2016
Edited: James Tursa
on 19 Dec 2016
Looks good, but depending on how you are actually building your reference empty structures, you might need to add the following also:
mxSetM(return_obj,1);
I.e., there may be a 0 in the M spot that you need to set to 1.
Regarding the methods used to get n_fields, the function mxGetNumberOfFields simply peeks at a location behind the pi pointer where the number of fields is stored as an int. So there is no counting involved. You only incur the function call overhead and a simple int pointer dereference. (The field names themselves are actually stored behind the pi pointer as well).
More Answers (2)
James Tursa
on 12 Dec 2016
Edited: James Tursa
on 13 Dec 2016
Here is some sample code to create a subset of a struct array inside a mex routine. One key point is that there are no official API functions that allow you to do this the same way that MATLAB does it at the m-file level. At the m-file level, MATLAB creates reference copies (a type of shared copy) of all of the field elements. This only involves incrementing a single counter inside the mxArray itself ... no deep data copy or even a new mxArray struct header is needed, so very efficient. The only thing available to you officially at the mex level is mxDuplicateArray, which could be a really nasty memory/performance hit. Hence this is one case where I would highly advise going outside the official API and using the undocumented API function mxCreateReference. This will allow you to efficiently construct the subset array exactly the same way MATLAB does at the m-file level. Code is listed below with a simple driver routine. You may want to modify how the code behaves in the case of invalid index inputs ... I just left that element NULL but you might want to generate an error.
/* Sample code to create subset of a struct array
*
* B = CreateStructSubset( S, x )
*
* S = a struct array
* x = a vector of indexes
* B = S(x)
*
* This mex routine does the equivalent of the B = S(x) statement at the
* m-code level. It uses the undocumented API function mxCreateReference
* to create reference copies of the "copied" contents rather than deep
* copies (just like MATLAB would do at the m-file level).
*
* Programmer: James Tursa
* Date: 12-Dec-2016
*
*/
/* Includes ----------------------------------------------------------- */
#include "mex.h"
/* Prototypes --------------------------------------------------------- */
mxArray *mxCreateReference(mxArray *); /* Undocumented API function */
mxArray *mxCreateStructSubset(const mxArray *mx, mwSize n, int *elements);
/* Gateway ------------------------------------------------------------ */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mxArray *mx_elements;
int i, n;
int *elements;
/* Example driver code. 1st input must be a struct. 2nd input must be a
* numeric variable, which is turned into an int32 just to get an equivalent
* int array to pass into the subset creation routine */
if( nrhs == 2 && mxIsStruct(prhs[0]) && mxIsNumeric(prhs[1]) ) {
mexCallMATLAB( 1, &mx_elements, 1, prhs+1, "int32" );
n = mxGetNumberOfElements(mx_elements);
elements = (int *) mxGetData(mx_elements);
for( i=0; i<n; i++ ) {
elements[i]--; /* Turn 1-based MATLAB indexing into 0-based C indexing */
}
plhs[0] = mxCreateStructSubset( prhs[0], n, elements );
mxDestroyArray(mx_elements);
} else {
mexErrMsgTxt("Invalid inputs");
}
}
/* Function to create a subset from an input struct. If an element value
* is outside the range of the dimensions of the input struct, that
* particular element is left empty and no copy is made. Could of course
* change this behavior to whatever you want instead (e.g., error).
* The elements array is assumed to be 0-based C indexing. */
mxArray *mxCreateStructSubset(const mxArray *mx, mwSize n, int *elements)
{
mxArray *result = NULL;
mwSize i, j, k, nelements, nfields, M, N;
mxArray **mdata, **rdata, **data;
const char **fieldnames;
if( mx && mxIsStruct(mx) ) { /* Make sure we have a struct to work with */
nfields = mxGetNumberOfFields(mx);
nelements = mxGetNumberOfElements(mx);
fieldnames = (char **) mxMalloc(nfields * sizeof(*fieldnames));
for( i=0; i<nfields; i++ ) {
fieldnames[i] = mxGetFieldNameByNumber(mx,i); /* Copy the fieldname pointers */
}
if( mxGetNumberOfDimensions(mx) == 2 && mxGetM(mx) == 1 ) {
M = 1; N = n;
} else {
M = n; N = 1;
}
result = mxCreateStructMatrix(M, N, nfields, fieldnames);
mxFree(fieldnames);
mdata = (mxArray **) mxGetData(mx);
rdata = (mxArray **) mxGetData(result);
for( i=0; i<n; i++ ) {
k = elements[i];
if( k >= 0 && k < nelements ) {
data = mdata + k * nfields;
for( j=0; j<nfields; j++ ) { /* For each element, copy all the fields */
if( *data ) {
*rdata++ = mxCreateReference(*data++); /* Make a reference copy, not a deep copy */
} else {
rdata++; data++; /* *data is NULL, so nothing to copy */
}
}
} else {
rdata += nfields; /* k is out of range, so leave all fields NULL */
}
}
}
return result;
}
1 Comment
Jan
on 16 Dec 2016
@James: Keep care. An angry armadillo has entered your room and types on your keyboard while you are away.
Jan
on 9 Dec 2016
You have to create a new struct array at first by mxCreateStructMatrix. You need the number of elements and number of fields as well as the field names in a char** obtained from the original struct. Then you copy the wanted fields in a loop from the old to the new struct. The code is not tricky, but tedious to type.
An excellent example for the beauty of Matlab! While s2 = s1(2:5) is simply nice, the C code looks like somebody had rolled an angry armadillo over the keyboard.
See Also
Categories
Find more on MATLAB Compiler in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!