2013-04-24 10 views
6

Я хотел бы перегрузить только один тип вызовов subsref (тип '()) для определенного класса и оставить любые другие вызовы Matlab, встроенные в subsref, в частности, я хочу, чтобы Matlab обрабатывал доступ к свойствам/методам через '' тип. Но похоже, что встроенная функция Matlab не работает, когда subsref перегружен в классе.Почему я не могу использовать builtin для классов, которые перегружают subsref?

Рассмотрим этот класс:

classdef TestBuiltIn 
    properties 
     testprop = 'This is the built in method'; 
    end 

    methods 
     function v = subsref(this, s) 
      disp('This is the overloaded method'); 
     end 
    end 
end 

Чтобы использовать перегруженный метод subsref, я делаю это:

t = TestBuiltIn; 
t.testprop 
    >> This is the overloaded method 

Это, как и ожидалось. Но теперь я хочу назвать Matlab встроенным методом subsref. Чтобы убедиться, что все правильно, сначала попробуйте аналогичный вызов по структуре:

x.testprop = 'Accessed correctly'; 
s.type = '.'; 
s.subs = 'testprop'; 
builtin('subsref', x, s) 
    >> Accessed correctly 

Это как и ожидалось. Но, когда я пытаюсь тот же метод на TestBuiltIn:

builtin('subsref', t, s) 
    >> This is the overloaded method 

... Matlab вызывает перегруженный метод, а не встроенный метод. Почему Matlab вызывает перегруженный метод, когда я попросил его вызвать встроенный метод?

ОБНОВЛЕНИЕ: В ответ на ответ @Andrew Janke это решение почти работает, но не совсем. Рассмотрим этот класс:

classdef TestIndexing 
    properties 
     prop1 
     child 
    end 

    methods 
     function this = TestIndexing(n) 
      if nargin==0 
       n = 1; 
      end 

      this.prop1 = n; 
      if n<2 
       this.child = TestIndexing(n+1); 
      else 
       this.child = ['child on instance ' num2str(n)]; 
      end 
     end 

     function v = subsref(this, s) 
      if strcmp(s(1).type, '()') 
       v = 'overloaded method'; 
      else 
       v = builtin('subsref', this, s); 
      end 
     end 
    end 
end 

Все это работает:

t = TestIndexing; 
t(1) 
    >> overloaded method 
t.prop1 
    >> 1 
t.child 
    >> [TestIndexing instance] 
t.child.prop1 
    >> 2 

Но это не работает; он использует встроенный в subsref для ребенка, а не перегруженный subsref:

t.child(1) 
    >> [TestIndexing instance] 

Обратите внимание, что данное поведение не согласуется с обоими из этих форм поведения (которые, как ожидается):

tc = t.child; 
tc(1) 
    >> overloaded method 

x.child = t.child; 
x.child(1) 
    >> overloaded method 
+0

Не уверен, что я полностью понимаю, но я думаю, что сначала вызывается встроенная функция, а затем в этом вызове вызывается метод перегрузки. Если возможно, установите точку останова в полезном месте. –

+0

Что-то совсем другое: надеюсь, вы просто попытаетесь сделать это из личного интереса, так как для большинства проблем перегрузка подзаголовка не может быть лучшим решением. –

+0

Я не уверен, что вы подразумеваете под встроенной функцией, которая вызывается первым - если это было гипотетически в случае (не уверен, как, но сказать), я думаю, мне нужно будет установить точку останова внутри встроенной функции subsref Matlab для проверки , Но это нативная функция, а не функция m, поэтому я не могу поставить точку останова. – Ben

ответ

4

Это возможно , IIRC. Чтобы изменить (), но не {} и '.', Напишите свой метод subsref, чтобы передать эти другие случаи вместе со встроенным subsref изнутри вашего перегруженного subsref, вместо того, чтобы явно вызвать встроенный извне.

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 

(И если это builtin вызов не работает, вы можете просто положить в тех случаях, которые используют . и {} непосредственно, так как перегрузка subsref игнорируется внутри определения класса.)

Чтобы сделать это полностью функциональный, вам может потребоваться изменить B на varargout и добавить поведение цепочки в случай «()».

+0

Спасибо, это почти работает, но не совсем - см. Обновление в моем вопросе – Ben

+0

Что происходит, в Matlab выражается выражение с несколькими индексами типа 'foo.bar.baz (3)' вместо того, чтобы оцениваться в шаги, такие как do 'foo.bar', а затем передать результат в' .baz', а затем передать этот результат в '(3)', проанализировать все выражение и передать его в один вызов subsref. (Это неинтуитивно.) Таким образом, ваш 'subsref' должен смотреть на S, обрабатывать S (1), а затем обрабатывать поведение« chaining », вручную передавая результаты вместе с« S (2: end) », а также другим вызовом 'subsref', который затем перехватит перегруженный метод. –

+0

Я изменил свой пример кода, чтобы показать поведение «цепочки». Это немного сложно, и я не могу проверить его на правильность, потому что сейчас у меня нет Matlab (извините), но это в основном то, что вам нужно делать. –

0

В целом. Вы должны использовать builtin(m,s) внутри функции, которая была перегружена. Это четко указано в документации MATLAB.

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

Встроенная (функция, x1, ..., хп) выполняет функцию встроенной с входных аргументов x1 через хп. Используйте встроенную функцию для выполнения оригинального встроенного метода, который перегружает функцию. Чтобы правильно работать , вы никогда не должны перегружать встроенную.

Рассмотрим этот код:

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 

и тест команды

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 
1

Чтобы расширить на объяснения, указанной на Mathworks board, встроенные работает только из перегруженного метода доступа к пре- существующий (встроенный) метод.

Перегрузка метод в Matlab эффективно затеняет встроенную реализацию от всех кроме метода делает затенение и метод делает слежку должен использовать встроенный доступ к встроенной реализации вместо рекурсии в себя.

0

В обновленной версии этой проблемы, когда вы звоните t.child(1), функция subsref получит аргумент s с s(1).type='.', s(1).subs='child' и s(2).type='()', s(2).subs='1'. Оценка этого выражения не выполняется поэтапно, как упоминалось в его ответе Andrew Janke. В результате при переопределении subsref вы должны обработать эту цепочку операций, сначала обработав «.». оператор. Вот неполный пример для случая,

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 

Вы также можете найти подробную образец кода и инструкции по Code Patterns for subsref and subsasgn Methods.

Еще одна вещь, которую вам, возможно, потребуется знать, состоит в том, что члены класса в этом классе также будут вызваны через subsref с '.' операция. Посмотрите на эту тему subsref on classes: how to dispatch methods?, вы обнаружите, что функция builtin не имеет возвращаемого значения (поскольку метод invoked не имеет возвращаемого значения). Однако возвращаемое значение builtin присваивается v (хотя v заменяется на varargout), что является очевидной ошибкой. Автор также дает временное решение, используя try ... catch для устранения этой ошибки.