Get a subset of a structure array in mex
1 次查看(过去 30 天)
显示 更早的评论
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.
采纳的回答
Jim Hokanson
2016-12-19
编辑:Jim Hokanson
2016-12-19
1 个评论
James Tursa
2016-12-19
编辑:James Tursa
2016-12-19
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).
更多回答(2 个)
James Tursa
2016-12-12
编辑:James Tursa
2016-12-13
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 个评论
Jan
2016-12-16
@James: Keep care. An angry armadillo has entered your room and types on your keyboard while you are away.
Jan
2016-12-9
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.
另请参阅
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!