As a toy example, I have a class that simply wraps a vector or matrix in an object and includes a timestamp of when it was created. I\'m trying to overload
When overloading the curly braces {}
to return a different number of output arguments than usual, it is also necessary to overload numel
to return the intended number (1, in this case). UPDATE: As of R2015b, the new function numArgumentsFromSubscript was created to be overloaded instead of numel
. The issue remains the same, but this function should be overloaded instead of numel
as I describe in the original answer below. See also the page "Modify nargout and nargin for Indexing Methods". Excerpt:
When a class overloads
numArgumentsFromSubscript
, MATLAB calls this method instead ofnumel
to compute the number of arguments expected forsubsref
nargout
andsubsasgn
nargin
.If classes do not overload
numArgumentsFromSubscript
, MATLAB callsnumel
to compute the values ofnargout
ornargin
.
More explanation of the underlying issue (need to specify number of output arguments) follows.
Original answer (use numArgumentsFromSubscript
instead of numel
for R2015b+)
To handle the possibility of a comma separated list of output arguments when indexing with curly braces, MATLAB calls numel
to determine the number of output arguments from the size of the input indexes (according to this MathWorks answer). If the number of output arguments in the definition of overloaded subsref
is inconsistent with (i.e. less than) the number provided by numel
, you get the "Too many output arguments" error. As stated by MathWorks:
Therefore, to allow curly brace indexing into your object while returning a number of arguments INCONSISTENT with the size of the input, you will need to overload the NUMEL function inside your class directory.
Since x{1:2}
normally provides two outputs (X{1},X{2}
), the definition function x = subsref(B, S)
is incompatible for this input. The solution is to include in the class a simple numel
method to overload the builtin function, as follows:
function n = numel(varargin)
n = 1;
end
Now the {}
indexing works as intended, mimicking ()
:
>> clear all % needed to reset the class definition
>> x = TimeStampValue(magic(3));
>> x(1:2)
ans =
7.355996e+05
8 3
>> x{1:2}
ans =
7.355996e+05
8 3
However, overloading curly braces in this manner is apparently a "specific type of code that we [MathWorks] did not expect customers to be writing". MathWorks recommends:
If you are designing your class to output only one argument, it is not recommended that you use curly brace indexing that requires you to overload NUMEL. Instead, it is recommended you use smooth brace () indexing.
UPDATE: Interestingly, the R2015b release notes state:
Before MATLAB release R2015b, MATLAB incorrectly computed the number of arguments expected for outputs from
subsref
and inputs tosubsasgn
for some indexing expressions that return or assign to a comma-separated list.With release R2015b, MATLAB correctly computes the values of
nargout
andnargin
according to the number of arguments required by the indexing expression.
So perhaps this is now fixed?
An alternative solution that comes to mind is to change function x = subsref(B, S)
to function varargout = subsref(B, S)
and adding varargout=cell(1,numel(B)); varargout{1} = x;
. As Amro noted in comments, pre-allocating the cell is necessary to avoid an error about an unassigned argument.
This solution seems to work in 2014b (but not entirely certain why)
classdef TestClass < handle
methods
function n = numel(~,varargin)
n = 1;
end
function varargout = subsref(input,S)
varargout = builtin('subsref',input,S);
end
function out = twoOutputs(~)
out = {}; out{1} = 2; out{2} = 3;
end
end
end
Then via the command window
>> testClass = TestClass();
>> [a,b] = testClass.twoOutouts()
a =
2
b =
3
I am working on a class to handle polynomials and polynomial matrices. I was having the same dificulty because I want different behaviors for the '.'
indexing in the cases of scalar polynomials and polynomial matrices.
In my case I want P.coef
to return a vector of coefficients if P
is a scalar polynomial. If P
is a polynomial matrix, P.coef
must return a cell array of the same size of P
, in which the cell {i,j}
contains the coefficient vector of the polynomial P(i,j)
.
The problem appeared when P.coef
was used with a matrix. My desired behavior returns only one object as an answer, but Matlab is expecting the function to return numel(P)
objects.
I found a very simple solution. When declaring subsref
, I used one mandatory output and a varargout
:
function [R,varargout] = subsref(P,S)
The body of the function defines R
as needed, according to my design. And at the very end of the function I added:
varargout(1:nargout-1) = cell(1,nargout-1);
To just return empty matrices as the extra outputs that Matlab wants.
This should create no problem if the function is always called with a single output argument, e.g., as in R = P.coef
. If the function is called without assigning, the user will see numel(P)-1
empty matrices, which is really not a big deal. Anyway, the user is warned about this in the function help.
I just ran into the same problem. What's even worse, is that the number of output arguments is enforced to be equal to what numel()
returns not only for the curly braces {}
, but also for the dot .
operation.
This means that if numel()
is overridden to return the usual prod(size(obj))
, it becomes impossible to access any properties of the underlying object (such as x.time
in the above example), as subsref()
is then expected to return multiple outputs.
But if numel()
just returns 1 instead, it does not match prod(size(obj))
, which is what most code working with numeric values or based on reshape()
expects. In fact, the MATLAB editor's balloon help immediately suggests that 'NUMEL(x) is usually faster than PROD(SIZE(x))', which suggest that they are equivalent, but apparently are not.
A possible solution would be to make numel()
return prod(size(obj))
and write explicit getter/setter functions for all these properties, e.g.,
x.get_time()
in the example above. This seems to work, because method calls apparently get resolved before subsref()
gets called. But then if one of the properties is a matrix it cannot be directly indexed any more because Matlab doesn't understand chained indexing, i.e., instead of writing
x.matrix(1,:)
one would have to write
m = x.get_matrix();
m(1,:)
which is ugly to say the least.
This is starting to get a bit frustrating. I still hope I've just overlooked something obvious, I can't believe that this is how it's supposed to work.