Get a subset of a structure array in mex
1 vue (au cours des 30 derniers jours)
Afficher commentaires plus anciens
Jim Hokanson
le 9 Déc 2016
Modifié(e) : James Tursa
le 19 Déc 2016
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 commentaires
Réponse acceptée
Jim Hokanson
le 19 Déc 2016
Modifié(e) : Jim Hokanson
le 19 Déc 2016
1 commentaire
James Tursa
le 19 Déc 2016
Modifié(e) : James Tursa
le 19 Déc 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).
Plus de réponses (2)
James Tursa
le 12 Déc 2016
Modifié(e) : James Tursa
le 13 Déc 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 commentaire
Jan
le 16 Déc 2016
@James: Keep care. An angry armadillo has entered your room and types on your keyboard while you are away.
Jan
le 9 Déc 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.
Voir également
Catégories
En savoir plus sur MATLAB Compiler dans Help Center et File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!