Thread Subject: How to modify a struct or cell array in a mex-file without using

Subject: How to modify a struct or cell array in a mex-file without using

From: Benjamin Schubert

Date: 4 Jun, 2007 04:09:38

Message: 1 of 4

Beware: quite long post
For the ASCII graphics below being properly displayed, it is
recommended
to use a fixed-width font.

This article describes how to change a cell of a cell array or a
field of a
structure array in a mex-function without duplicating the whole
array.
Since the right-hand-side arguments must not be changed, one
typically
creates a deep copy of the array using the function
"mxDuplicateArray" and
changes the field in the copy. A more cunning way is to create a
shared copy,
making it uncessery to copy the fields or cells not being changed.

The internal structure of how Matlab stores a variable and the
concept of
sharing variables using crosslinks is described in an article from
Peter Boetcher:
 <http://groups.google.com/group/comp.soft-sys.matlab/browse_thread/thread/c241d8821fb90275/47189d498d1f45b8?lnk=st&q=&rnum=1&hl=en#47189d498d1f45b8>

For creating shared copies of an mxArray, the functions
"mxCreateSharedCopy", "mxUnshareArray", and "mxUnreference" are used.
Since not documented, the following might not be correct. But it has
been
tested and seems to work for Matlab versions R14, 2006b, 2007a. Any
ideas
about how doing it better are welcome.

Examine the disassembly, I guess that the three functions have the
following syntax:

  mxArray *mxCreateSharedCopy(const mxArray *pr);
  bool mxUnshareArray(const mxArray *pr, const bool noDeepCopy);
// true if not successful
  mxArray *mxUnreference(const mxArray *pr);

They are described in the following. The const declaration is not
fully
correct since the array headers change. But with it, the compiler
does
not complain about different const declarations when assigning
right-hand-side
arguments.

Take a variable x created in Matlab:
  x = {[1 2], [3 4]};

In a C-Mex-File, x looks as follows:

  mxArray *x;
x points to an mxArray whose data pointer points to an array of two
pointers, each pointing to an mxArray (an mxArray is indicated by a
rectangle):
*x:
+==================+
| crosslink = NULL |
| refcount = 0 |
| pdata ----------+--------> [mxArray* mxArray*]
+==================+ | |
                                  \ / \ /
                            +==================+
+==================+
                            | crosslink = NULL | | crosslink =
NULL |
                            | refcount = 0 | | refcount = 0
 |
                            | pdata -+ | | pdata -+
 |
                            +=========|========+
+=========|========+
                                      | |
                                     \ / \ /
                                    [1 2] [3 4]

If we want to change the content of the first cell, we first must
create
a copy of x. The following procedere is equivalent for structure
arrays,
except the mxGetCell and mxSetCell functions must be replaced.

  mxArray *y = mxCreateSharedCopy(x);
The data pointer of y points to the same array as x does. A crosslink

is set between x and y:
*x:
+==================+
| crosslink = y |
| refcount = 0 |
| pdata ----------+--------> [mxArray* mxArray*]
+==================+ / \ | |
                        | \ / \ /
                        | +==================+
+==================+
                        | | crosslink = NULL | | crosslink =
NULL |
                        | | refcount = 0 | | refcount = 0
 |
                        | | pdata -+ | | pdata -+
 |
                        | +=========|========+
+=========|========+
                        | | |
                        | \ / \ /
                        |

Subject: How to modify a struct or cell array in a mex-file without using "mxDuplicateArray"

From: schubert.ing@googlemail.com

Date: 4 Jun, 2007 11:36:20

Message: 2 of 4

I'm sorry the last message has been messed up due to inserted
linebreaks. This one should look better.

Beware: quite long post
For the ASCII graphics below being properly displayed, it is
recommended to use a fixed-width font.

This article describes how to change a cell of a cell array or a field
of a structure array in a mex-function without duplicating the whole
array. Since the right-hand-side arguments must not be changed, one
typically creates a deep copy of the array using the function
"mxDuplicateArray" and changes the field in the copy. A more cunning
way is to create a shared copy, making it uncessery to copy the fields
or cells not being changed.


The internal structure of how Matlab stores a variable and the concept
of sharing variables using crosslinks is described in an article from
Peter Boetcher:
http://groups.google.com/group/comp.soft-sys.matlab/browse_thread/thread/c241d8821fb90275/47189d498d1f45b8?lnk=st&q=&rnum=1&hl=en#47189d498d1f45b8

For creating shared copies of an mxArray, the functions
"mxCreateSharedCopy", "mxUnshareArray", and "mxUnreference" are used.
Since not documented, the following might not be correct. But it has
been tested and seems to work for Matlab versions R14, 2006b, 2007a.
Any ideas about how doing it better are welcome.

Examine the disassembly, I guess that the three functions have the
following syntax:

  mxArray *mxCreateSharedCopy(const mxArray *pr);
  bool mxUnshareArray(const mxArray *pr, const bool noDeepCopy); //
true if not successful
  mxArray *mxUnreference(const mxArray *pr);

They are described in the following. The const declaration is not
fully correct since the array headers change. But with it, the
compiler does not complain about different const declarations when
assigning right-hand-side arguments.


Take a variable x created in Matlab:
  x = {[1 2], [3 4]};

In a C-Mex-File, x looks as follows:

  mxArray *x;
x points to an mxArray whose data pointer points to an array of two
pointers, each pointing to an mxArray (an mxArray is indicated by a
rectangle):
*x:
+==================+
| crosslink = NULL |
| refcount = 0 |
| pdata ----------+------> [mxArray* mxArray*]
+==================+ | |
                                \ / \ /
                          +==================+ +==================+
                          | crosslink = NULL | | crosslink = NULL |
                          | refcount = 0 | | refcount = 0 |
                          | pdata -+ | | pdata -+ |
                          +=========|========+ +=========|========+
                                    | |
                                   \ / \ /
                                  [1 2] [3 4]


If we want to change the content of the first cell, we first must
create a copy of x. The following procedere is equivalent for
structure arrays, except the mxGetCell and mxSetCell functions must be
replaced.

  mxArray *y = mxCreateSharedCopy(x);
The data pointer of y points to the same array as x does. A crosslink
is set between x and y:
*x:
+==================+
| crosslink = y |
| refcount = 0 |
| pdata ----------+------> [mxArray* mxArray*]
+==================+ / \ | |
                       | \ / \ /
                       | +==================+ +==================+
                       | | crosslink = NULL | | crosslink = NULL |
                       | | refcount = 0 | | refcount = 0 |
                       | | pdata -+ | | pdata -+ |
                       | +=========|========+ +=========|========+
                       | | |
                       | \ / \ /
                       | [1 2] [3 4]
*y: |
+==================+ |
| crosslink = x | |
| refcount = 0 | |
| pdata ----------+---+
+==================+



  mxUnshareArray(y, true); // true: do not make a deep copy
The array of mxArray-pointers is copied and the crosslink between x
and y is removed. The reference counter (refcount) of each mxArray in
the cell is increased by one.
*x:
+==================+
| crosslink = NULL |
| refcount = 0 |
| pdata ----------+------> [mxArray* mxArray*]
+==================+ | |
                             \ / \ /
                          +==================+ +==================+
                          | crosslink = NULL | | crosslink = NULL |
                          | refcount = 1 | | refcount = 1 |
                          | pdata -+ | | pdata -+ |
                          +=========|========+ +=========|========+
                             / \ | / \ |
                              | \ / | \ /
                              | [1 2] | [3 4]
*y: | |
+==================+ | |
| crosslink = NULL | | |
| refcount = 0 | | |
| pdata ----------+------> [mxArray* mxArray*]
+==================+



Now we want to change the first cell of y. The shared mxArray has to
be copied using the function "mxUnreference".
Get the first mxArray:
  mxArray *first_cell = mxGetCell(y, 0);
Note that the following yields the same result, since the mxArray only
exists once (y is replaced by x):
  mxArray *first_cell = mxGetCell(x, 0);

The mxUnreference function copies the mxArray and creates a crosslink
between the copies. The affected reference counter is decreased by 1:
  mxArray *first_cell_copy = mxUnreference(first_cell);
*x:
+==================+
| crosslink = NULL |
| refcount = 0 |
| pdata ----------+------> [mxArray* mxArray*]
+==================+ | |
                             \ / \ /
                          +==================+ +==================+
              *first_cell:| crosslink = f_c_c| | crosslink = NULL |
                          | refcount = 0 | | refcount = 1 |
                          | pdata -+ | | pdata -+ |
                          +=========|========+ +=========|========+
                                    | <-----+ / \ |
                                   \ / | | \ /
                                  [1 2] | | [3 4]
*y: | |
+==================+ | |
| crosslink = NULL | | |
| refcount = 0 | | |
| pdata ----------+------> [mxArray* | mxArray*]
+==================+ | |
                             \ / |
                          +=================|+
       *first_cell_copied:| crosslink = f_c ||
                          | refcount = 0 ||
                          | pdata ---------+|
                          +==================+


To assign a new value to y{1}, the crosslink between first_cell and
first_cell_copied must be removed. This can be done by either
destroying the mxArray first_cell_copy and assigning a new one to
y{1}:
  mxDestroyArray(first_cell_copy);
  mxSetCell(y, 0, new_mxArray);
or by unsharing it and assign the data pointer:
  mxUnshareArray(first_cell_copy);
  mxSetData ...

If first_cell is another cell or structure array, one can recursively
repeat all the steps above (after mxCreateShareCopy). It is noteworthy
that all this can also be applied on objects, since objects in Matlab
are stored as structure arrays.

Below is a program demonstrating the use of the three functions.
Suggestions for improvements are welcome.

I hope this is useful to someone. In my case, it gives a great
performance benefit when modifying a certain property of a large
number of objects.





Example program:
/
************************************************************************
 * syntax in Matlab:
 * function var_struct_copied = foo(var_struct, value,
duplicate_array)
 *
 * Returns the structure variable "var_struct" with the first field
set to
 * "value". If "duplicate_array" is false or omitted, a shared copy is
 * created. Otherwise a deep copy of the struct array is returned
 * (using "mxDuplicateArray").
 
*************************************************************************/


#include "mex.h"

// declare undocumented functions
extern mxArray *mxCreateSharedCopy(const mxArray *pr);
extern bool mxUnshareArray(const mxArray *pr, const bool noDeepCopy);
extern mxArray *mxUnreference(const mxArray *pr);


/* Definition of structure mxArray_tag for debugging purposes. Might
not
 * be fully correct for Matlab 2006b or 2007a, but it works.
 * Thanks to Peter Boettcher. */
struct mxArray_tag {
  const char *name;
  mxClassID class_id;
  int vartype;
  mxArray *crosslink;
  int number_of_dims;
  int refcount;
  struct {
    unsigned int scalar_flag : 1;
    unsigned int flag1 : 1;
    unsigned int flag2 : 1;
    unsigned int flag3 : 1;
    unsigned int flag4 : 1;
    unsigned int flag5 : 1;
    unsigned int flag6 : 1;
    unsigned int flag7 : 1;
    unsigned int private_data_flag : 1;
    unsigned int flag8 : 1;
    unsigned int flag9 : 1;
    unsigned int flag10 : 1;
    unsigned int flag11 : 4;
    unsigned int flag12 : 8;
    unsigned int flag13 : 8;
  } flags;
  int rowdim;
  int coldim;
  union {
    struct {
      double *pdata; // original: void*
      double *pimag_data; // original: void*
      void *irptr;
      void *jcptr;
      int nelements;
      int nfields;
    } number_array;
    struct {
      mxArray **pdata;
      char *field_names;
      void *dummy1;
      void *dummy2;
      int dummy3;
      int nfields;
    } struct_array;
    struct {
      void *pdata; /*mxGetInfo*/
      char *field_names;
      char *name;
      int checksum;
      int nelements;
      int reserved;
    } object_array;
  } data;
};



// Matlab gatway function
void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
{
    const mxArray *p_in,
                  *mx_value; // value to be assigned
    mxArray *p_return; // mxArray to be returned
    bool duplicate_array = false; // duplicate or shallow?



    if (nrhs < 2) {
        mexErrMsgTxt("Two input arguments are expected.");
    } else {
        p_in = prhs[0];
        mx_value = prhs[1];
        if (!mxIsStruct(p_in))
            mexErrMsgTxt("First argument must be a structure array.");
    }

    if (nrhs == 3) {
        const mxArray *mx_arg = prhs[2];
        if (mxIsLogicalScalar(mx_arg)) {
            duplicate_array = *mxGetLogicals(mx_arg);
        } else
            mexErrMsgTxt("Third argument must be a logical scalar.");
    }


    if (duplicate_array) {
        // duplicate given arrays, set value and return
        p_return = mxDuplicateArray(p_in);
        mxSetFieldByNumber(p_return, 0, 0,
mxDuplicateArray(mx_value));

    } else {
        mxArray *mx_field, *mx_field_shared;

        // create shared copy
        p_return = mxCreateSharedCopy(p_in);

        // unshare array
        mxUnshareArray(p_return, true);

        // unreference first field
        mx_field = mxGetFieldByNumber(p_return, 0, 0);
        mx_field_shared = mxUnreference(mx_field);
        // mxArray *mx_field has been copied and is shared
(crosslinked) with *mx_field_shared

        // destroy shared array (removes crosslink), since we want to
set a new one
        mxDestroyArray(mx_field_shared);

        // set new array
        mxSetFieldByNumber(p_return, 0, 0,
mxCreateSharedCopy(mx_value));
    }

    plhs[0] = p_return;
}

Subject: How to modify a struct or cell array in a mex-file without using

From: Andrew

Date: 5 Nov, 2007 07:56:56

Message: 3 of 4

I thought I'd add a note that you have to be careful how
you call the example. If you try:

var_struct_copied = foo(var_struct(2), value,
duplicate_array);

it will cause badness. As far as I can work out Matlab
recognises that var_struct(2) is a temporary variable, so
the line:

p_return = mxCreateSharedCopy(p_in);

sets p_return = p_in;

Unsharing p_return and destroying its fields is then bad.
I add a line to check if (p_return == p_in) and if it is
just set the fields of p_return without any of the
unsharing (complete abuse of the const declaration since it
doesn't matter because the input is temporary). If there
are nested structures you still need the unsharing for them
though (if you were altering var_struct(2).a.b you need the
shared copy of var_struct(2).a).

I also had a question about the second part of this
statement:

> y{1}:
> mxDestroyArray(first_cell_copy);
> mxSetCell(y, 0, new_mxArray);
> or by unsharing it and assign the data pointer:
> mxUnshareArray(first_cell_copy);
> mxSetData ...

I have been using:
mxDestroyArray(first_cell_copy);
mxSetCell(y, 0, mxDuplicateArray(first_cell));

Can I use first_cell_copy instead? Something like:
mxUnshareArray(first_cell_copy);
mxSetCell(y, 0, first_cell_copy);

I'm not sure how to use the mxSetData here...

It seems I'm making two copies of the first cell.

Cheers,
Andrew








 schubert.ing@googlemail.com wrote in message
<1180956980.030482.78450@n4g2000hsb.googlegroups.com>...
> I'm sorry the last message has been messed up due to
inserted
> linebreaks. This one should look better.
>
> Beware: quite long post
> For the ASCII graphics below being properly displayed, it
is
> recommended to use a fixed-width font.
>
> This article describes how to change a cell of a cell
array or a field
> of a structure array in a mex-function without
duplicating the whole
> array. Since the right-hand-side arguments must not be
changed, one
> typically creates a deep copy of the array using the
function
> "mxDuplicateArray" and changes the field in the copy. A
more cunning
> way is to create a shared copy, making it uncessery to
copy the fields
> or cells not being changed.
>
>
> The internal structure of how Matlab stores a variable
and the concept
> of sharing variables using crosslinks is described in an
article from
> Peter Boetcher:
> http://groups.google.com/group/comp.soft-
sys.matlab/browse_thread/thread/c241d8821fb90275/47189d498d1
f45b8?lnk=st&q=&rnum=1&hl=en#47189d498d1f45b8
>
> For creating shared copies of an mxArray, the functions
> "mxCreateSharedCopy", "mxUnshareArray",
and "mxUnreference" are used.
> Since not documented, the following might not be correct.
But it has
> been tested and seems to work for Matlab versions R14,
2006b, 2007a.
> Any ideas about how doing it better are welcome.
>
> Examine the disassembly, I guess that the three functions
have the
> following syntax:
>
> mxArray *mxCreateSharedCopy(const mxArray *pr);
> bool mxUnshareArray(const mxArray *pr, const bool
noDeepCopy); //
> true if not successful
> mxArray *mxUnreference(const mxArray *pr);
>
> They are described in the following. The const
declaration is not
> fully correct since the array headers change. But with
it, the
> compiler does not complain about different const
declarations when
> assigning right-hand-side arguments.
>
>
> Take a variable x created in Matlab:
> x = {[1 2], [3 4]};
>
> In a C-Mex-File, x looks as follows:
>
> mxArray *x;
> x points to an mxArray whose data pointer points to an
array of two
> pointers, each pointing to an mxArray (an mxArray is
indicated by a
> rectangle):
> *x:
> +==================+
> | crosslink = NULL |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ | |
> \ /
\ /
> +==================+
+==================+
> | crosslink = NULL | |
crosslink = NULL |
> | refcount = 0 | |
refcount = 0 |
> | pdata -+ | |
pdata -+ |
> +=========|========+
+=========|========+
>
| |
>
\ / \ /
> [1 2]
[3 4]
>
>
> If we want to change the content of the first cell, we
first must
> create a copy of x. The following procedere is equivalent
for
> structure arrays, except the mxGetCell and mxSetCell
functions must be
> replaced.
>
> mxArray *y = mxCreateSharedCopy(x);
> The data pointer of y points to the same array as x does.
A crosslink
> is set between x and y:
> *x:
> +==================+
> | crosslink = y |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ / \ | |
> | \ /
\ /
> | +==================+
+==================+
> | | crosslink = NULL | |
crosslink = NULL |
> | | refcount = 0 | |
refcount = 0 |
> | | pdata -+ | |
pdata -+ |
> | +=========|========+
+=========|========+
> |
| |
> |
\ / \ /
> | [1 2]
[3 4]
> *y: |
> +==================+ |
> | crosslink = x | |
> | refcount = 0 | |
> | pdata ----------+---+
> +==================+
>
>
>
> mxUnshareArray(y, true); // true: do not make a
deep copy
> The array of mxArray-pointers is copied and the crosslink
between x
> and y is removed. The reference counter (refcount) of
each mxArray in
> the cell is increased by one.
> *x:
> +==================+
> | crosslink = NULL |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ | |
> \ / \ /
> +==================+
+==================+
> | crosslink = NULL | |
crosslink = NULL |
> | refcount = 1 | |
refcount = 1 |
> | pdata -+ | |
pdata -+ |
> +=========|========+
+=========|========+
> / \ | /
\ |
> | \ /
| \ /
> | [1 2] |
[3 4]
> *y: | |
> +==================+ | |
> | crosslink = NULL | | |
> | refcount = 0 | | |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+
>
>
>
> Now we want to change the first cell of y. The shared
mxArray has to
> be copied using the function "mxUnreference".
> Get the first mxArray:
> mxArray *first_cell = mxGetCell(y, 0);
> Note that the following yields the same result, since the
mxArray only
> exists once (y is replaced by x):
> mxArray *first_cell = mxGetCell(x, 0);
>
> The mxUnreference function copies the mxArray and creates
a crosslink
> between the copies. The affected reference counter is
decreased by 1:
> mxArray *first_cell_copy = mxUnreference(first_cell);
> *x:
> +==================+
> | crosslink = NULL |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ | |
> \ / \ /
> +==================+
+==================+
> *first_cell:| crosslink = f_c_c| |
crosslink = NULL |
> | refcount = 0 | |
refcount = 1 |
> | pdata -+ | |
pdata -+ |
> +=========|========+
+=========|========+
> | <-----+ /
\ |
> \ / |
| \ /
> [1 2] | |
[3 4]
> *y: | |
> +==================+ | |
> | crosslink = NULL | | |
> | refcount = 0 | | |
> | pdata ----------+------> [mxArray* |
mxArray*]
> +==================+ | |
> \ / |
> +=================|+
> *first_cell_copied:| crosslink = f_c ||
> | refcount = 0 ||
> | pdata ---------+|
> +==================+
>
>
> To assign a new value to y{1}, the crosslink between
first_cell and
> first_cell_copied must be removed. This can be done by
either
> destroying the mxArray first_cell_copy and assigning a
new one to
> y{1}:
> mxDestroyArray(first_cell_copy);
> mxSetCell(y, 0, new_mxArray);
> or by unsharing it and assign the data pointer:
> mxUnshareArray(first_cell_copy);
> mxSetData ...
>
> If first_cell is another cell or structure array, one can
recursively
> repeat all the steps above (after mxCreateShareCopy). It
is noteworthy
> that all this can also be applied on objects, since
objects in Matlab
> are stored as structure arrays.
>
> Below is a program demonstrating the use of the three
functions.
> Suggestions for improvements are welcome.
>
> I hope this is useful to someone. In my case, it gives a
great
> performance benefit when modifying a certain property of
a large
> number of objects.
>
>
>
>
>
> Example program:
> /
>
************************************************************
************
> * syntax in Matlab:
> * function var_struct_copied = foo(var_struct, value,
> duplicate_array)
> *
> * Returns the structure variable "var_struct" with the
first field
> set to
> * "value". If "duplicate_array" is false or omitted, a
shared copy is
> * created. Otherwise a deep copy of the struct array is
returned
> * (using "mxDuplicateArray").
>
>
************************************************************
*************/
>
>
> #include "mex.h"
>
> // declare undocumented functions
> extern mxArray *mxCreateSharedCopy(const mxArray *pr);
> extern bool mxUnshareArray(const mxArray *pr, const bool
noDeepCopy);
> extern mxArray *mxUnreference(const mxArray *pr);
>
>
> /* Definition of structure mxArray_tag for debugging
purposes. Might
> not
> * be fully correct for Matlab 2006b or 2007a, but it
works.
> * Thanks to Peter Boettcher. */
> struct mxArray_tag {
> const char *name;
> mxClassID class_id;
> int vartype;
> mxArray *crosslink;
> int number_of_dims;
> int refcount;
> struct {
> unsigned int scalar_flag : 1;
> unsigned int flag1 : 1;
> unsigned int flag2 : 1;
> unsigned int flag3 : 1;
> unsigned int flag4 : 1;
> unsigned int flag5 : 1;
> unsigned int flag6 : 1;
> unsigned int flag7 : 1;
> unsigned int private_data_flag : 1;
> unsigned int flag8 : 1;
> unsigned int flag9 : 1;
> unsigned int flag10 : 1;
> unsigned int flag11 : 4;
> unsigned int flag12 : 8;
> unsigned int flag13 : 8;
> } flags;
> int rowdim;
> int coldim;
> union {
> struct {
> double *pdata; // original: void*
> double *pimag_data; // original: void*
> void *irptr;
> void *jcptr;
> int nelements;
> int nfields;
> } number_array;
> struct {
> mxArray **pdata;
> char *field_names;
> void *dummy1;
> void *dummy2;
> int dummy3;
> int nfields;
> } struct_array;
> struct {
> void *pdata; /*mxGetInfo*/
> char *field_names;
> char *name;
> int checksum;
> int nelements;
> int reserved;
> } object_array;
> } data;
> };
>
>
>
> // Matlab gatway function
> void mexFunction(int nlhs, mxArray *plhs[],
> int nrhs, const mxArray *prhs[])
> {
> const mxArray *p_in,
> *mx_value; // value to be
assigned
> mxArray *p_return; // mxArray to be
returned
> bool duplicate_array = false; // duplicate or
shallow?
>
>
>
> if (nrhs < 2) {
> mexErrMsgTxt("Two input arguments are expected.");
> } else {
> p_in = prhs[0];
> mx_value = prhs[1];
> if (!mxIsStruct(p_in))
> mexErrMsgTxt("First argument must be a
structure array.");
> }
>
> if (nrhs == 3) {
> const mxArray *mx_arg = prhs[2];
> if (mxIsLogicalScalar(mx_arg)) {
> duplicate_array = *mxGetLogicals(mx_arg);
> } else
> mexErrMsgTxt("Third argument must be a
logical scalar.");
> }
>
>
> if (duplicate_array) {
> // duplicate given arrays, set value and return
> p_return = mxDuplicateArray(p_in);
> mxSetFieldByNumber(p_return, 0, 0,
> mxDuplicateArray(mx_value));
>
> } else {
> mxArray *mx_field, *mx_field_shared;
>
> // create shared copy
> p_return = mxCreateSharedCopy(p_in);
>
> // unshare array
> mxUnshareArray(p_return, true);
>
> // unreference first field
> mx_field = mxGetFieldByNumber(p_return, 0, 0);
> mx_field_shared = mxUnreference(mx_field);
> // mxArray *mx_field has been copied and is shared
> (crosslinked) with *mx_field_shared
>
> // destroy shared array (removes crosslink),
since we want to
> set a new one
> mxDestroyArray(mx_field_shared);
>
> // set new array
> mxSetFieldByNumber(p_return, 0, 0,
> mxCreateSharedCopy(mx_value));
> }
>
> plhs[0] = p_return;
> }
>

Subject: How to modify a struct or cell array in a mex-file without using

From: Benjamin Schubert

Date: 9 Dec, 2007 23:05:19

Message: 4 of 4

Hello Andrew,
sorry for answering so late, I have just found your response.

"Andrew " <awbsmith@itee.uq.edu.au> wrote in message
<fgmic8$7il$1@fred.mathworks.com>...
> I thought I'd add a note that you have to be careful how
> you call the example. If you try:
>
> var_struct_copied = foo(var_struct(2), value,
> duplicate_array);
>
> it will cause badness. As far as I can work out Matlab
> recognises that var_struct(2) is a temporary variable, so
> the line:
>
> p_return = mxCreateSharedCopy(p_in);
>
> sets p_return = p_in;
>
> Unsharing p_return and destroying its fields is then bad.
> I add a line to check if (p_return == p_in) and if it is
> just set the fields of p_return without any of the
> unsharing (complete abuse of the const declaration since it
> doesn't matter because the input is temporary). If there
> are nested structures you still need the unsharing for them
> though (if you were altering var_struct(2).a.b you need the
> shared copy of var_struct(2).a).

Yes, you are right. I stumbled across the case of temporal
arrays after writing this article. But there seems to be a
solution: using "mxCreateSharedDataCopy" instead of
"mxCreateSharedCopy". In most cases, these functions behave
equally. They do not if the mxArray to be copied is marked
as being a temporal array. Since it gets destroyed after the
function is finished, one gets into trouble here. Using the
function "mxCreateSharedDataCopy" instead works as intended.
It really creates a shared copy and the mxArray does not get
destroyed. I have rewritten the article above here:
http://www.mk.tu-berlin.de/Members/Benjamin/mex_sharedArrays

> I also had a question about the second part of this
> statement:
>
> > y{1}:
> > mxDestroyArray(first_cell_copy);
> > mxSetCell(y, 0, new_mxArray);
> > or by unsharing it and assign the data pointer:
> > mxUnshareArray(first_cell_copy);
> > mxSetData ...
>
> I have been using:
> mxDestroyArray(first_cell_copy);
> mxSetCell(y, 0, mxDuplicateArray(first_cell));
>
> Can I use first_cell_copy instead? Something like:
> mxUnshareArray(first_cell_copy);
> mxSetCell(y, 0, first_cell_copy);
>
> I'm not sure how to use the mxSetData here...
>
> It seems I'm making two copies of the first cell.
>
The last line of code is not necessary, since the first
entry in y already points to the mxArray "first_cell_copy".
What happens after the mxUnshareArray(first_cell_copy)
command is that the data vector "[1 2]" has been copied and
the crosslink between "first_cell" and "first_cell_copy" has
been removed.
mxSetData means that you can assign new data to the first
cell of y
  mxSetCell(y, 0, pointer to an mxArray),
but you must keep in mind the data which "first_cell_copy"
points to. It still is in anywhere in memory until removed...

> Cheers,
> Andrew
>
Regards,
Benjamin

Tags for this Thread

Everyone's Tags:

Add a New Tag:

Separated by commas
Ex.: root locus, bode

What are tags?

A tag is like a keyword or category label associated with each thread. Tags make it easier for you to find threads of interest.

Anyone can tag a thread. Tags are public and visible to everyone.

Tag Activity for This Thread
Tag Applied By Date/Time
mxunsharearray Benjamin Schubert 9 Dec, 2007 18:10:30
mxunreference Benjamin Schubert 9 Dec, 2007 18:10:30
mxduplicatearray Benjamin Schubert 9 Dec, 2007 18:10:30
mxcreatesharedcopy Benjamin Schubert 9 Dec, 2007 18:10:30
mxcreateshareddatacopy Benjamin Schubert 9 Dec, 2007 18:10:30
shared copy Benjamin Schubert 9 Dec, 2007 18:10:30
cross link Benjamin Schubert 9 Dec, 2007 18:10:29
rssFeed for this Thread

Public Submission Policy

NOTICE: Any content you submit to MATLAB Central, including personal information, is not subject to the protections which may be afforded information collected under other sections of The MathWorks, Inc. Web site. You are entirely responsible for all content that you upload, post, e-mail, transmit or otherwise make available via MATLAB Central. The MathWorks does not control the content posted by visitors to MATLAB Central and, does not guarantee the accuracy, integrity, or quality of such content. Under no circumstances will The MathWorks be liable in any way for any content not authored by The MathWorks, or any loss or damage of any kind incurred as a result of the use of any content posted, e-mailed, transmitted or otherwise made available via MATLAB Central. Read the complete Disclaimer prior to use.

Contact us at files@mathworks.com