Simple newbie C mex question..
3 views (last 30 days)
Show older comments
Hi, I'm (very) new to C and need to write a C++ mex file that inputs and outputs some cell arrays. So, I'm trying to get my head around what I need to do to transfer and manipulate them in the correct format and so on. So, below is my simple 'noddy' test program, that will accept a cell array and then tell me how many dimensions it has. I then try to pass the same array to a small function to do the same thing. This compiles, but when I run it I get the following....
>> cc = {[1,2,3],[4,5,6],[7 8 9]};
>> gateWayTest2(cc)
Inside gateway , var has 3 dims
Inside function, var has 1 dims
>>
So, I would expect 3 dims in both. What am I doing wrong here? (I can partially answer my own question in that I'm very new to C and am struggling to get my head around pointers...). But anyway, the stage I want to get to is that I can do some maths on the contents of 'cc' in the function, and then output this modified 'cc' back into Matlab. So if anyone could show me how I could do something simple like add a constant to some of the elements and then output that, that would be a really really useful! Many thanks in advance, Arwel
#include "mex.h"
void testFunction(mxArray *in)
{
double *numberOfContrasts;
numberOfContrasts = mxGetNumberOfElements(in);
mexPrintf("Inside function, var has %d dims \n",numberOfContrasts);
}
void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
{
double *numberOfContrasts;
mxArray *xdata;
/*Check for correct number and type of input arguments*/
if (nrhs != 1) {
mexErrMsgIdAndTxt("MATLAB:cellTest:invalidNumInputs",
"One input argument required");
};
if (mxIsCel!=1) {
mexErrMsgIdAndTxt("MATLAB:cellTest:invalidType",
"Cell array required");
};
/*Print how many dimensions in cell array as a test*/
numberOfContrasts = mxGetNumberOfElements(prhs[0]);
mexPrintf("Inside gateway , var has %d dims \n",numberOfContrasts);
/*Pass to a function to a simple fn make sure it 'arrives' in the correct format*/
xdata = mxGetPr(prhs[0]);
testFunction(xdata);
return;
}
0 Comments
Answers (5)
James Tursa
on 12 Jul 2016
Edited: James Tursa
on 12 Jul 2016
A few problems:
1) This code is wrong:
double *numberOfContrasts;
numberOfContrasts = mxGetNumberOfElements(in);
mexPrintf("Inside function, var has %d dims \n",numberOfContrasts);
The return value of mxGetNumberOfElements is size_t, but you are assigning that return value into a pointer_to_double. Then you print the pointer with a %d format which is intended for integer types. There is no need to use a pointer here. Simply use the same type as the return value. E.g.,
size_t numberOfContrasts; // <-- changed type to size_t
numberOfContrasts = mxGetNumberOfElements(in);
mexPrintf("Inside function, var has %d dims \n",numberOfContrasts);
2) Same problem in your mexFunction code:
double *numberOfContrasts; // <-- Need to change (double *) to size_t
:
numberOfContrasts = mxGetNumberOfElements(prhs[0]);
mexPrintf("Inside gateway , var has %d dims \n",numberOfContrasts);
3) You are passing a data pointer to a routine that expects an mxArray pointer:
xdata = mxGetPr(prhs[0]);
testFunction(xdata);
In the above code, the data pointer for prhs[0] points to a memory block that contains three mxArray addresses. It does not point to memory block that contains an mxArray. (In fact, I would not have been surprised if this code had crashed MATLAB with an invalid memory access violation.) The correct way to retrieve the address of this data block would be:
mxArray **cellblock;
:
cellblock = (mxArray **) mxGetData(prhs[0]);
That is, cellblock is a pointer, and it points to memory that contain (mxArray *) types.
All that being said, you don't really want to be doing this anyway in your code. All you want is to pass prhs[0] directly. E.g.,
testFunction(prhs[0]);
This should generate a compiler warning since you are passing a const value to a routine that is not expecting a const value. You can correct that by doing this:
void testFunction(const mxArray *in)
4) I assume this is a typo:
if (mxIsCel!=1) {
and you really have this in your code:
if (mxIsCell(prhs[0]) != 1) {
5) Finally, I would strongly advise that when you mex your code you use the verbose option -v. That way a lot of these programming errors that may only appear as warnings to the compiler will be shown on the screen to you.
0 Comments
Arwel
on 12 Jul 2016
3 Comments
James Tursa
on 13 Jul 2016
OK. So can you elaborate just a bit on the calculations you will be doing? Are you thinking of manipulating a cell array input variable in-place? And multi-threading that work out? Both of these are not easy in a mex routine for two main reasons:
(1) It can be very difficult to tell when a cell element is shared with another variable in a mex routine, but you need to know this in order to manipulate the variable properly. There is really nothing in the API that you can use for this ... you have to hack into the mxArray variable structure. Not pretty.
(2) None of the API function that allocate memory are thread-safe. So if you were thinking of multi-threading anything that involved creating cell elements you can forget that right now ... it would likely mess up the MATLAB Memory Manager and crash MATLAB. The only practical solution is to create all of the cell elements up front, before you enter the parallel section, and then in the parallel section you can fill in the data.
Arwel
on 19 Jul 2016
3 Comments
James Tursa
on 4 Aug 2016
You can't do anything inside of a parallel loop that uses the MATLAB Memory Manager to allocate memory. So this line is trouble:
calcOutpArray = mxCreateDoubleMatrix(points,n,ComplexFlag)
The other API calls inside your parallel loop (mxGetCell, mxGetM, mxGetN, mxGetPr) do not allocate memory so they should be OK in a parallel loop.
The mxSetCell call should also be OK since I don't think it will allocate memory, but I am not 100% sure about this. To my knowledge, this line:
call mxSetCell(outputArray,i,calcOutpArray)
does the following:
- Puts the calcOutpArray mxArray address into the i'th spot of outputArray.
- Changes the type of calcOutpArray to SUB_ELEMENT (this is done in-place, no memory allocation required)
- Removes the calcOutpArray mxArray address from the garbage collection list (again, I don't see how this act could result in memory allocation)
But the reason I am not 100% sure about memory allocation is that since R2011b MATLAB has included something in the pi data area of cell arrays. That pointer used to be NULL for cell arrays, but now it has something in it and I have no idea what that is. If there is something in the background there that allocates memory for some reason, then mxSetCell would be a problem in a parallel loop. (If that turns out to be the case, it is an easy workaround to just save the calcOutpArray mxArray pointers off to the side inside the parallel loop and then use mxSetCell on all of them after the parallel loop)
BOTTOM LINE:
You need to rewrite this parallel loop so that there is no MATLAB Memory Manager allocations inside of it. You basically need to do all of the anticipated mxCreateDoubleMatrix etc calls ahead of time and then just fill in the data inside the parallel loop.
Arwel
on 5 Aug 2016
1 Comment
James Tursa
on 8 Aug 2016
Edited: James Tursa
on 8 Aug 2016
I think you need to make your scalar variables private in the parallel loop. By default, I don't think OpenMP makes these variables private. So try adding a private clause to your OpenMP meta command and make the following variables private: thisCell_Xdat, points, pr_in_xdata, thisCell_sld, layers, pr_in_sld, calcOutpArray, pr_out. I think the iteration index variable i is the only variable that is private by default.
Also, these lines are doing extra work that is not needed (and in fact create a temporary memory leak):
plhs(1) = mxCreateCellMatrix(m,n)
outputArray = mxCreateCellMatrix(m,n)
:
plhs(1) = mxDuplicatearray(outputArray)
You have created three separate cell matrices, two of which get garbage collected (otherwise they would have leaked). You only need one cell matrix here. E.g.,
plhs(1) = mxCreateCellMatrix(m,n)
outputArray = plhs(1)
:
! plhs(1) = mxDuplicatearray(outputArray) <-- Get rid of this line
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!