Some time ago, I needed to modify a property value of a class object. The problem was that this property was declared as private
and for some reason my client could not modify the originating classdef to make this property accessible.
Problem definition
We start with a very simple class definition:
classdef MyClass properties (SetAccess=private) %GetAccess = public y end properties (Access=private) %GetAccess = SetAccess = private x end methods function obj = MyClass(x,y) % constructor if nargin>0, obj.x = x; end if nargin>1, obj.y = y; end end function result = isPropEqualTo(obj,propName,value) result = (obj.(propName)==value); end end end |
The problem is simple: we need to both get and set the value of inaccessible private properties x,y. But following object construction, MyClass
enables direct read access only to property y, and write access to neither of its properties:
>> obj = MyClass(3,4) obj = MyClass with properties: y: 4 >> obj.x You cannot get the 'x' property of MyClass. >> obj.x=5 You cannot set the 'x' property of MyClass. >> obj.y=5 You cannot set the read-only property 'y' of MyClass. |
A dead end, would you say? – Well, it never stopped us before, has it? After all, is it not the raison-d’être of this blog?
Reading private properties
Getting the value of x is simple enough when we recall that calling Matlab’s struct function on a class object reveals all its hidden treasures. I wrote about this a couple of years ago, and I’m not sure how many people realize the power of this undocumented feature:
>> s = struct(obj) Warning: Calling STRUCT on an object prevents the object from hiding its implementation details and should thus be avoided. Use DISP or DISPLAY to see the visible public details of an object. See 'help struct' for more information. (Type "warning off MATLAB:structOnObject" to suppress this warning.) s = y: 4 x: 3 |
As the warning mentions, we should not do this often (bad, bad boy!). If we must (I promise I had a good reason, ma!), then we can simply turn off the nagging warning:
warning off MATLAB:structOnObject |
We can now read all the private internal properties of the object. Yummy!
Setting private properties
The natural attempt would now be to update the struct’s fields with new values. Unfortunately, this does not affect the original class properties, since our struct is merely a copy of the original. Even if our original object is a handle class, the struct would still be a shallow copy and not a real reference to the object data.
Mex’s standard mxGetProperty cannot be used on the original object, because mxGetProperty returns a copy of the property (not the original reference – probably to prevent exactly what I’m describing here…), and in any case it can’t access private properties. mxSetProperty is a dead-end for similar reasons.
The core idea behind the solution is Matlab’s Copy-on-Write mechanism (COW). This basically means that when our struct is created, the field values actually hold references (pointers) to the original object properties. It is only when trying to modify the struct fields that COW kicks in and a real copy is made. This is done automatically and we do not have any control over it. However, we can use this information to our advantage by retrieving the field references (pointers) before COW has a chance to ruin them. Once we have the reference to the private data, we can modify the data in-place using a bit of Mex.
So the trick is to get the reference address (pointer) of s.x
and s.y
. How do we do that?
We can use another trick here, which is a corollary to the COW mechanism: when we pass s.x
into a function, it is not a data copy that is being passed (by value), but rather its pointer (by reference). So we can simply get this pointer in our Mex function and use it to modify the original property value. Easy enough, right?
Not so fast. Don’t forget that s.x
is merely a reference copy of the original property data. If we modify s.x
‘s reference we’re just killing the so-called cross-link of the shared-array. What we need to do (more or less) is to traverse this cross-link back to its source, to get the real reference to the data.
Sounds complicated? Well, it is a bit. Luckily, Mex guru James (Jim) Tursa comes to the rescue with his mxGetPropertyPtr function on the File Exchange, which does all that for us. Once we have it compiled (the utility automatically Mex-compiles itself on first use), we can use it as follows (note the highlighted line using mxGetPropertyPtr):
/* Place in mxMySetProperty.c and mex-compile*/ #include "mex.h" #include "mxGetPropertyPtr.c" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { mxArray *x; register double *xpr; int buflen; char *propName = "x"; double newValue = -3.14159265358979; if ( nrhs > 1 ) { /* Get the specified property name (or "x" if not specified) */ buflen = mxGetNumberOfElements(prhs[1]) + 1; propName = mxCalloc(buflen, sizeof(char)); mxGetString(prhs[1], propName, buflen); } /* Get the pointer to the original property data */ x = mxGetPropertyPtr(prhs[0],0,propName); if ( !x ) { mexErrMsgTxt("Failed to get pointer to property."); } /* Display the existing property value */ xpr = mxGetPr(x); mexPrintf("existing value of property %s = %f\n", propName, *xpr); /* Update the property with the new value */ if ( nrhs > 2 ) { /* Get the specified new value (or -pi if not specified) */ double *pr = mxGetPr(prhs[2]); newValue = *pr; } mexPrintf("setting value of property %s to %f\n", propName, newValue); *xpr = newValue; } |
Naturally, this simplistic Mex function should also be made to accept non-scalar values. This is left as an exercise to the reader.
The usage in Matlab of this mxMySetProperty function is super simple:
% Update obj.x from 3 => pi/2 >> mxMySetProperty(s,'x',pi/2); existing value of property x = 3.000000 setting value of property x to 1.570796 % Update obj.y from 4 => -5 >> mxMySetProperty(s,'y',-5); % here we can also use obj instead of s since obj.y is accessible existing value of property y = 4.000000 setting value of property y to -5.000000 % Check that the struct copy has been updated correctly >> s s = y: -5 x: 1.5707963267949 % Check that the original object's private properties have been updated correctly >> obj obj = MyClass with properties: y: -5 >> obj.isPropEqualTo('x',pi/2) ans = 1 % ==true |
Jim Tursa has promised to supply a mxSetPropertyPtr variant of his mxGetPropertyPtr for the past two years (1,2,3,4). It will surely be more robust than my simplistic mxMySetProperty function, so I look forward to finally seeing it on FEX!
Conclusion
With some dirty tricks and undocumented hacks, we can both get and set private-access object properties. Please don’t do this unless you have a really good reason (such as a customer breathing down your neck, who doesn’t give a fig that his properties were declared private…).
The mechanism shown above can also be used to improve performance when updating public object properties, since it updates the data in-place rather than create a copy. This could be significant when the property size is very large (multi-MB), since it avoids unnecessary memory allocation and deallocation. You might think that with public properties we could use the standard mxGetProperty for this, but as I said above this function apparently returns a copy of the data, not a direct reference. Also note that last month I discussed additional performance aspects of accessing object properties.
This blog will now take a short break for the holidays. I hope you had a good ride this year, see you again on the other side of 2013.
Merry Christmas and happy New-Year everybody!
Just an small additional comment on “Reading private properties”. If for whatever reason you do not want
s = struct(obj)
to reveal the structure, you can define inside the methods block the method “struct”Then
s = struct(obj)
will show nothing (or an error, or…) . But there is a solution for this too:s = builtin('struct',obj)
.Best regards
[…] mxCreateUninitDoubleMatrix, mxCreateUninitNumericArray, mxCreateUninitNumericMatrix and mxGetPropertyShared. Unfortunately, mxSerialize and mxDeserialize remain among the functions that were left out, which […]
These files are no longer available
what can I do?
mxGetPropertyPtr.c
mxGetPropertyPtr.h
I believe that you are referring to James Tursa’s submission – you can get it here: https://www.mathworks.com/matlabcentral/fileexchange/30672-mxgetpropertyptr. Inside the submission, use the files mexPropertyPtrSDC.c, mexPropertyPtrSDC.h. The submission contains various Matlab and C test files that can help understand how to use the code, as well as a PDF document explaining the technical details. Note that the submission files have not been updated since 2019 and may possibly fail on recent Matlab releases; they rely on deeply undocumented aspects, which may easily break across Matlab releases.
The link at https://www.mathworks.com/matlabcentral/fileexchange/30672-mxgetpropertyptr
no longer contain the files
mxGetPropertyPtr.c
mxGetPropertyPtr.h
I downloaded the folder. They are missing
see the details in my reply above