Accessing private object properties

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:

Inaccessible private property (or is it?)

Inaccessible private property (or is it?)

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!

Categories: High risk of breaking in future versions, Mex, Undocumented feature

Tags: , , ,

Bookmark and SharePrint Print

2 Responses to Accessing private object properties

  1. Mauricio says:

    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

    methods
        function s = struct(obj)
            % ... do nothing...., or show error or...
        end
    end

    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

  2. Pingback: Serializing/deserializing Matlab data | Undocumented Matlab

Leave a Reply

Your email address will not be published. Required fields are marked *