A MEX solution to split classdef-defined object arrays into cells

As an experiment with MEX files, I am trying to create a mex routine that will take an array A of classdef-defined objects and return a cell array C whos elements are the individual elements of the input array, i.e., C{i}=A(i). I know this can be done conventionally with num2cell, but I am looking for a mex alternative.
Using a rudimentary classdef,
classdef fakeclass
properties
a=2
end
end
and the following mex code obj2cell.c,
#include "mex.h"
#include "matrix.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
// Check the number of input and output arguments
if (nrhs != 1)
{
mexErrMsgIdAndTxt("objectArrayToCell:invalidInput", "One input argument is required.");
}
// Get the input object array
const mxArray *inputArray = prhs[0];
// Get the number of elements in the input array
mwSize numElements = mxGetNumberOfElements(inputArray);
// Create a cell array to store the elements
plhs[0] = mxCreateCellMatrix(1, numElements);
// Copy each element from the object array to the cell array
for (mwIndex i = 0; i < numElements; ++i)
{
mxSetCell(plhs[0], i, mxDuplicateArray(mxGetCell(inputArray, i)));
}
}
I performed the following test, but it gives only empty results.
>> C=obj2cell([fakeclass,fakeclass])
C =
1×2 cell array
{0×0 double} {0×0 double}
Can someone point out the flaw in my code?

 Accepted Answer

You need to use mxGetProperty( ) to get at the properties of a classdef object. And since this returns a deep copy, no need for the subsequent mxDuplicateArray( ). E.g.,
mxSetCell(plhs[0], i, mxGetProperty(inputArray, i, "a"));
And of course in your actual code you would need to put in some checks to ensure that prhs[0] is indeed the proper class with the expected property. E.g.,
if( !mxIsClass(inputArray,"fakeclass") ) {
// error message
}
I don't think there is an API function for checking if a property exists. You simply have to examine the returned value of mxGetProperty( ) to see if it is NULL or not. Or you could simply let the routine fill the cell array with NULL values (which is legit). At the MATLAB level, NULL elements are converted to empty 0x0 doubles on the fly when accessed. You would have to decide if this behavior is acceptable or not when requesting non-existent properties. You may want to check which properties the object has at the MATLAB level and pass that in as a 2nd input argument to tell the mex routine which property to get.

12 Comments

Thanks James,
And of course in your actual code you would need to put in some checks to ensure that prhs[0] is indeed the proper class with the expected property.
I am hoping to make something that works for arbitrary classes. Is there definitely no way to clone a complete and arbitrary classdef-defined object within a MEX? And, if I must work one property at a time, is there no automatic way to get the list of properties of the class, similar to fieldnames() in MCode?
Additionally, I don't really require for my purposes that the cell array contain deep copies. Soft references to the individual object array elements would be enough.
To reply to this comment first:
"Additionally, I don't really require for my purposes that the cell array contain deep copies. Soft references to the individual object array elements would be enough."
HA HA HA!!! ROFL!!! Good luck with that! MATLAB has chosen to hide their classdef properties somewhere within the bowels of their mxArrays. I have complained about the lack of pointer access to these properties for years! But nothing has been provided. Access to classdef objects (either getting or setting) inside a mex routine is required to use deep copies. There are no exceptions, and because the details are so well hidden there are no hacks available either. (Well, I had some hack code for this on the FEX, but it no longer works because of the changes they have made to mex interfaces over the years ... maybe it was my hack code that inspired them to bury things deeper, who knows?) This makes working with large classdef objects inside a mex routine slow and memory inefficient to say the least. Sorry to give you the bad news ...
As an aside, the old @directory method of defining classes was much more friendly to mex programmers, since the object was really just a thinly wrapped struct and you could get and set the fields with the API struct functions. You could have fast memory efficient code to deal with these class objects in a mex routine. Definitely a step backwards for mex programmers when classdef was introduced.
As far as getting the property names inside a mex routine, there is no specific API function for this but you can call back to MATLAB with mexCallMATLAB( ) to get them. E.g., something like this:
mxArray *propertynames;
mexCallMATLAB(1, &propertynames, 1, &inputArray, "properties");
would get you a cell array of char strings with all the property names. You can get at them with mxGetCell(propertynames,i) etc.
And if you need C-style char string versions of these to pass into the mxGetProperty( ) function downstream, you can use mxArrayToString(mxGetCell(propertynames,i)) for that.
Ah well. Much more complicated than I'd hope.
Thanks a lot!
E.g., mxSetCell(plhs[0], i, mxGetProperty(inputArray, i, "a"));
Your example doesn't show how mxSetProperty would be used. Are you sure you didn't mean something like,
mxArray *value;
for (mwIndex i = 0; i < numElements; ++i)
{
value=mxGetProperty(inputArray, i, "a");
mxSetProperty(plhs[0], i, "a", value);
}
But if so, how does plhs[0] "know" that it is an instance of the same class as inputArray? In other words, how do I preallocate it as an object of the same type?
No. mxGetProperty( ) and mxSetProperty( ) are only to be used with classdef objects. But you want the output of your mex routine to be a cell array, so you should be creating a cell array (which you did with mxCreateCellMatrix) and filling the elements with mxSetCell as shown.
That being said, I am a bit confused by your question. plhs[0] is a cell array and thus has nothing to do with class objects and doesn't need to know where value comes from. In your case it happens to come from a classdef object property, but that is irrelevant as far as plhs[0] is concerned. It could come from anywhere. As long as value is a newly created mxArray (which it will be if it comes from mxGetProperty) everything will be fine. What you can't do is directly attach a read-only mxArray from one of your prhs[ ] inputs to the cell array. That would create an extra shallow copy of the variable that the MATLAB Memory Manager doesn't know about and will eventually lead to a MATLAB crash when MATLAB tries to free already free'd memory downstream. If you want to attach mxArray variables that come from prhs[ ] variables to plhs[ ] variables, the only sanctioned method is to create deep copies of them first. There is a way to hack around this and safely attach shallow copies (like what happens at the m-code level), but I will not go into that now. Some background details:
Whenever you create new mxArray variables in your mex routine (via mxCreateEtc, mxDuplicateArray, mxGetProperty, etc) the address of that mxArray goes on the mex mxArray Garbage Collection List. When the mex function returns to the caller (either normally or through an error condition), all mxArrays on the Garbage Collection List are automatically destroyed. There is also a mex Memory Garbage Collection List that functions similarly for allocated raw memory via mxMalloc, mxCalloc, and mxRealloc.
What mxGetCell and friends mxGetField etc. do:
They get the actual mxArray address residing in the cell or struct variable data area. I.e., you get a copy of the mxArray pointer that is in the cell or struct variable element. No deep copies involved.
What mxSetCell and friends mxSetField etc. do:
They attach the value mxArray address directly to the cell or struct in question (i.e., not a deep copy). Then they remove the value mxArray address from the mex mxArray Garbage Collection list. That way when the mex function returns to the caller the value mxArray doesn't get wiped out in the process. Stuff that is in the plhs[ ] return variable isn't destroyed in a normal return, but it is in an error return.
What mxGetProperty does:
It gets a deep copy of the classdef object property and places the address of this newly created mxArray on the mex mxArray Garbage Collection List. When the mex function returns to the caller this newly created mxArray will be automatically destroyed unless you have done something with it that removes it from this List (such as attaching it to a cell array).
What mxSetProperty does:
It attaches a deep copy of the mxArray to the classdef object. Note this is very different from the behavior of mxSetCell and friends.
That being said, I am a bit confused by your question. plhs[0] is a cell array and thus has nothing to do with class objects
plhs[0] is a cell array whose contents are intended to be classdef-defined objects. In other words, I want this call,
A=[fakeclass,fakeclass,fakeclass];
C=obj2cell(A)
to result in this,
C =
1×3 cell array
{1×1 fakeclass} {1×1 fakeclass} {1×1 fakeclass}
In other other words, I want the user defined objects A(i) to be split up and allocated to the contents of separate cell array elements C{i}. (Let's assume the C{i} will be deep copies of A(i), although that's not what I really prefer).
You seem to have said that the only way to do this is in MEX code is to allocate new fakeclass instances to be held by each C{i} and deep copy the property data from A(i) one property at a time to the fakeclass object in C{i}. If I have that right, then the question is, how do you allocate a new fakeclass instance inside the MEX code. There is no mxCreateClassdefObject() API function, as far as I know.
OK, this is a different direction entirely. I thought your original post was asking how to extract properties from a classdef object array and build a cell array from them. My bad. So you are saying you want the classdef object elements themselves to be used for building a cell array. This is different, of course, and I'm going to have to think about it. There is no classdef equivalent of mxGetData in the API, so you may have to call back to MATLAB and use subsref( ) to get this done. I will follow up with another post later ...
so you may have to call back to MATLAB and use subsref( ) to get this done.
I'm trying to avoid subsref. Is there any way that I could use something like the following to obtain a copy of the i-th object array element inputArray(i)? if so, would the scalarCopy be a value-semantic copy, a handle-semantic copy, or a deep-copy?
mxArray *scalarCopy;
mexCallMATLAB(1, scalarCopy, 1, mxGetCell(inputarray, i), "mycopy");
function x=mycopy(x) %An mfile
end
I haven't had a chance to test any code yet. But to answer a basic question, everything dealing with classdef objects inside a mex routine is going to involve deep copies. And everything returned from a mexCallMATLAB( ) call will be a deep copy, even if the same operation at the m-file level is a shallow copy. E.g., calling reshape( ) on a full variable via mexCallMATLAB( ) will get you a deep copy result even though the same operation at the m-file level would get you a shallow copy. MATLAB stacks everything against you in the C-mex interface ...
Well, even though you don't feel too keen on subsref( ) and don't want deep copies, that is the only way I know how to do what you want at the moment. Here is the C-code:
// Bare bones function to turn array into cell array of elements
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
const mwSize *dims;
mwSize ndim;
mxArray *S, *subs, *lhs, *ix;
mxArray *rhs[2];
double *pr;
const char *fieldnames[2] = {"type","subs"};
const char parens[] = "()";
size_t i, n;
if( nrhs ) {
// The indexing struct
S = mxCreateStructMatrix(1, 1, 2, fieldnames); // create the struct
mxSetFieldByNumber(S, 0, 0, mxCreateString(parens)); // set the S.type
subs = mxCreateCellMatrix(1,1); // create the subs cell
ix = mxCreateDoubleScalar(0); // create a dummy scalar
pr = (double *) mxGetData(ix); // get pointer to dummy scalar data
mxSetCell(subs,0,ix); // attach dummy scalar to subs cell
mxSetFieldByNumber(S, 0, 1, subs); // set the S.subs
// Create the output variable
dims = mxGetDimensions(prhs[0]);
ndim = mxGetNumberOfDimensions(prhs[0]);
plhs[0] = mxCreateCellArray(ndim, dims);
n = mxGetNumberOfElements(prhs[0]);
// Set up for indexing call
rhs[0] = (mxArray *) prhs[0];
rhs[1] = S;
// Loop over elements
for( i=0; i<n; i++ ) {
*pr = i+1; // set the index we are trying to get (MATLAB 1-based)
mexCallMATLAB(1,&lhs,2,rhs,"subsref"); // Extract the element (deep copy)
mxSetCell(plhs[0],i,lhs); // Attach the element to output
}
mxDestroyArray(S); // destroy the S struct
}
}
And here is a sample run:
>> d1 = fakeclass; d1.a = 1;
>> d2 = fakeclass; d2.a = 2;
>> d3 = fakeclass; d3.a = 3;
>> fake = [d1,d2,d3];
>> c = obj2cell(fake)
c =
1×3 cell array
{1×1 fakeclass} {1×1 fakeclass} {1×1 fakeclass}
>> c{1}
ans =
fakeclass with properties:
a: 1
>> c{2}
ans =
fakeclass with properties:
a: 2
>> c{3}
ans =
fakeclass with properties:
a: 3
There is nothing special about what type of object gets passed into the mex function. As long as you can get elements of it with subsref, the mex function should probably work with it. E.g., here is just an ordinary double array example:
>> d = 1:3;
>> c = obj2cell(d)
c =
1×3 cell array
{[1]} {[2]} {[3]}
(Of course, having gone to all this trouble using subsref from a mex routine, it should be pointed out that it would be simpler to simply use mexCallMATLAB to call back and use num2cell instead)
And everything returned from a mexCallMATLAB( ) call will be a deep copy, even if the same operation at the m-file level is a shallow copy.
Too bad. Well, at least it might be a more succinct way of deep-copying an object than trying to determine and loop over all the properties individually.

Sign in to comment.

More Answers (0)

Categories

Products

Release

R2021b

Asked:

on 12 Sep 2023

Commented:

on 19 Sep 2023

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!