Problem with subreferencing (subsref) operator overload

6 views (last 30 days)
I am experiencing a problem with operator overloading of the subreferencing operator for a class. Below is an illustrative MATLAB value class that has a property, x, that stores a vector of integers. Operator overloading for () is implemented by overloading the subsref function.
The documentation for subsref is here: http://www.mathworks.com/help/matlab/ref/subsref.html
classdef BizarreClass
properties
x;
end
methods
function obj = BizarreClass()
obj.x = [1 2 3 4 5];
end
function obj = setx(obj, x_val)
disp('called setx')
obj.x = x_val;
end
function varargout = subsref(obj, S)
%Ignore function calls and only tend to subreferences
if (strcmp(S(1).type, '()'))
disp('called for the right reason')
varargout{1} = obj.x(S(1).subs{1});
else
disp('called for the wrong reason')
varargout = {};
end
end
end
end
Now let's say I instantiate BizarreClass:
b = BizarreClass()
And then call the setx function to set x to a different value:
b = b.setx([1 3 5])
which gives an error:
Error in BizarreClass>BizarreClass.subsref (line 18)
if (strcmp(S(1).type, '()'))
Error using BizarreClass/subsref
Output argument "varargout" (and maybe others) not assigned during call to
"c:\users\johndoe\Desktop\swallowing\trunk\matlab_scripts\BizarreClass.m>BizarreClass.subsref".
This is happening because subsref is being called first (as it should). That is why I have logic that checks the input variable S and only handle the case where the object is subreferenced like b(3), in which case varargout gets the value indexed into the array, x. Otherwise, varargout is empty, which is the problem: The 'b = ' in 'b = setx([1 3 5])' needs to be there for the value of b to be updated. Unfortunately, this calls subsref first and not the setx method, causing that error.
I can get past this by calling setx on b like this:
b = setx(b, [1 3 5])
But I want to call it like:
b = b.setx([1 3 5])
Is there a solution? How do I not have subsref to be called for any case other than the subsref operator? Or is there a design pattern that I should use that will allow me to use b = b.setx([1 3 5])
  1 Comment
John
John on 2 Sep 2014
I should add, it appears that when I call:
b.setx([1 3 5])
the subsref function is called but not the setx function even though subsref function returns.

Sign in to comment.

Answers (1)

Guillaume
Guillaume on 2 Sep 2014
Edited: Guillaume on 2 Sep 2014
Unfortunately, once you overload subsref to handle (), you also have to handle . and {} in your overload:
b.method(...)
will only call subsref and not method anymore.
The simple thing to do is to error out in subsref when you S.type is '.' (or '{}') and output a message that dot notation is not supported (that's what matwhorks do in several of their classes, see the implementation of table for example) and use the function notation which always work as you found out.
The other option is to basically reimplement method calls from within subsref. The problem with that is because the call is within subsref it bypasses the access modifier (i.e. it then becomes possible to access private / protected methods) unless you also implement that within subsref. It's a can of worms.
Personally, I find the implementation of subsref completely broken (it should be 3 different functions) and instead of overloading (), I just implement a method GetAt that does what the overload would do. You loose the syntactic elegance of () but you also avoid a lot of pain.

Categories

Find more on Numeric Types in Help Center and File Exchange

Tags

Community Treasure Hunt

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

Start Hunting!