Why can't I use builtin for classes that overload subsref?

落爺英雄遲暮 提交于 2019-12-03 03:28:05

It's possible, IIRC. To change () but not {} and '.', write your subsref method to pass those other cases along to the builtin subsref from within your overloaded subsref, instead of trying to explicitly call the builtin from outside.

function B = subsref(A, S)
    % Handle the first indexing on your obj itself
    switch S(1).type
        case '()'
            B = % ... do your custom "()" behavior ...
        otherwise
            % Enable normal "." and "{}" behavior
            B = builtin('subsref', A, S(1))
        end
    end
    % Handle "chaining" (not sure this part is fully correct; it is tricky)
    orig_B = B; % hold on to a copy for debugging purposes
    if numel(S) > 1
        B = subsref(B, S(2:end)); % regular call, not "builtin", to support overrides
    end
end

(And if that builtin call doesn't work, you can just put in cases that use . and {} directly, because the subsref overload is ignored inside the class definition.)

To make it fully functional, you may need to change B to a varargout, and add chaining behavior in to the "()" case.

To expand on the explanation given on the Mathworks board, builtin only works from within an overloaded method to access the pre-existing (built in) method.

Overloading a method in Matlab effectively shadows the built in implementation from everything except the method doing the shadowing, and the method doing the shadowing must use builtin to access the built in implementation instead of recursing into itself.

In general. You should use builtin(m,s) inside the function that is been overloaded. This is specified clearly in MATLAB documentation.

http://www.mathworks.com/help/matlab/ref/builtin.html

builtin(function,x1,...,xn) executes the built-in function with the input arguments x1 through xn. Use builtin to execute the original built-in from within a method that overloads the function. To work properly, you must never overload builtin.

Consider this code:

classdef TestBuiltIn
    properties
        testprop = 'This is the built in method';
        testprop2 = 'This is the derived subsref ';
    end
    methods

        function v = subsref(m, s)
            disp('enter subsref no matter how!');
            v = builtin('subsref',m, s);
        end
    end
end

and test command

clear;
t = TestBuiltIn;
builtin('subsref', t, s)
s.type = '.';
s.subs = 'testprop';
s2 = s;
s2.subs = 'testprop2';

>> builtin('subsref', t, s1)

enter subsref no matter how!

ans =

This is the derived subsref 

>> builtin('subsref', t, s)
enter subsref no matter how!

ans =

This is the built in method

In your updated version of this problem, when you call t.child(1), the subsref function will receive argument s with s(1).type='.', s(1).subs='child' and s(2).type='()', s(2).subs='1'. The evaluation of this expression is not in a step-by-step manner, as Andrew Janke mentioned in his answer. As a result, when overriding subsref, you should handle this operation chain, by first processing the '.' operator. Here is a incomplete example for your case,

function v = subsref(this, s)
    switch s(1).type
       case '.'
           member = s(1).subs;
           if ismethod(this, member)
               % invoke builtin function to access method member
               % there is issue about the number of output arguments
               v = builtin('subsref',this,s);
           elseif isprop(this, member) % property
               if length(s) == 1
                   % invoke builtin function to access method member
                   v = builtin('subsref', this, s);
               elseif length(s) == 2 && strcmp(s(2).type,'()')
                   % this is where you evaluate 'tc.child(1)'
               else
                   % add other cases when you need, otherwise calling builtin
               end
           else
               % handling error.
           end
       case '()'
           % this is where you evaluate 't(1)'
           % you may need to handle something like 't(1).prop1', like the '.' case
       otherwise
           % by default, calling the builtin.
    end
end

You can also find a detailed code sample and instruction at Code Patterns for subsref and subsasgn Methods.

One more thing you may need to know, is that method members in this class will also be invoked through subsref with '.' operation. Look at this subject subsref on classes: how to dispatch methods?, you will find that the builtin function has no return value (since the invoked method has no return value). However, the return value of builtin is assigned to v(even though v is replaced by varargout), which is an obvious error. The author also gives a temporary solution by using try ... catch to resolve this error.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!