2013-04-24 6 views
6

Ich möchte nur eine Art von Subsref-Aufrufe (der '()' Typ) für eine bestimmte Klasse überladen und lassen Sie alle anderen Aufrufe Matlab eingebaute subsref - speziell möchte ich Matlab mit Eigentum/Methoden-Zugriff über die "." Art. Aber es scheint, als ob Matlabs "eingebaute" Funktion nicht funktioniert, wenn subsref in einer Klasse überladen wird.Warum kann ich builtin nicht für Klassen verwenden, die subsref überladen?

Betrachten Sie diese Klasse:

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 

die überladene subsref Methode zu verwenden, kann ich dies tun:

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

, die als erwartet ist. Aber jetzt möchte ich die eingebaute Subref-Methode von Matlab aufrufen. Um sicherzustellen, dass ich die Dinge richtig mache, probiere ich zuerst einen ähnlichen Aufruf an einer Struktur aus:

Das ist wie erwartet. Aber, wenn ich die gleiche Methode auf TestBuiltIn versuchen:

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

... Matlab ruft die überladene Methode anstelle der in Verfahren gebaut. Warum ruft Matlab die überladene Methode auf, wenn ich anforderte, dass sie die eingebaute Methode aufruft?

UPDATE: Als Antwort auf @Andrew Janke Antwort, diese Lösung funktioniert fast, aber nicht ganz. Betrachten Sie diese Klasse:

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 

All dies funktioniert:

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

Aber das funktioniert nicht; Es nutzt die für das Kind in subsref gebaut und nicht überlasteten subsref:

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

Beachten Sie, dass das oben beschriebene Verhalten mit diesen beiden Verhaltensweisen unvereinbar ist (die wie erwartet):

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

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

Nicht sicher, ob ich vollständig verstehe, aber ich denke, dass zuerst die eingebaute Funktion aufgerufen wird, und dann in diesem Aufruf die Überlademethode aufgerufen wird. Wenn möglich, setzen Sie einen Haltepunkt an einer nützlichen Stelle. –

+0

Etwas völlig anderes: Ich hoffe, du versuchst dies nur aus persönlichem Interesse zu tun, da die meisten Probleme beim Überladen von subsref nicht die beste Lösung sind. –

+0

Ich bin mir nicht sicher, was Sie meinen, wenn die eingebaute Funktion zuerst aufgerufen wird - wenn das hypothetisch der Fall wäre (nicht sicher, wie, aber sagen), würde ich einen Breakpoint in Matlabs eingebauter Subsref-Funktion setzen müssen, um dies zu verifizieren . Aber das ist eine native Funktion und keine m-Funktion, deshalb kann ich dort keinen Haltepunkt setzen. – Ben

Antwort

4

Es ist möglich, , IIRC. Um (), aber nicht {} und '.' Zu ändern, schreiben Sie Ihre subsref Methode, um diese anderen Fälle an die eingebaute Subsref innerhalb Ihrer überladenen Subsref zu übergeben, anstatt zu versuchen, das Builtin explizit von außen aufzurufen.

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 

(Und wenn der builtin Anruf, man kann nur in Fällen setzt nicht funktioniert, die . und {} direkt verwenden, da die subsref Überlastung innerhalb der Klassendefinition wird ignoriert.)

zu machen Wenn Sie voll funktionsfähig sind, müssen Sie möglicherweise B in ein Varargout ändern und das Verkettungsverhalten in den Fall "()" einfügen.

+0

Danke, das funktioniert fast, aber nicht ganz - bitte sehen Sie das Update in meiner Frage – Ben

+0

Was ist los in Matlab, ein Ausdruck mit Mehrfachindexierung wie 'foo.bar.baz (3)', anstatt in ausgewertet zu werden Schritte wie 'foo.bar' zu tun und dann das Ergebnis an' .baz' zu übergeben und dann dieses Ergebnis an '(3) 'zu übergeben, wird den gesamten Ausdruck analysieren und ihn in einem einzigen Aufruf an subsref übergeben. (Dies ist nicht intuitiv.) Also muss Ihr 'subsref' S betrachten, S (1) handhaben und dann das" Verkettungs "-Verhalten handhaben, indem es die Ergebnisse zusammen mit' S (2: end) 'zusammen mit einem anderen Aufruf von 'subsref', die dann die überladene Methode übernimmt. –

+0

Ich habe meinen Beispielcode geändert, um "Verkettung" anzuzeigen. Es ist ein bisschen knifflig und ich kann es nicht auf Korrektheit testen, weil ich Matlab gerade nicht habe (Entschuldigung), aber das ist im Grunde, was Sie tun müssen. –

0

Im Allgemeinen. Sie sollten builtin(m,s) innerhalb der Funktion verwenden, die überlastet wurde. Dies ist in der MATLAB-Dokumentation eindeutig festgelegt.

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

builtin (Funktion, x1, ..., xn) führt die eingebaute Funktion mit dem Eingabeargumente x1 bis xn. Verwenden Sie builtin, um das ursprüngliche -Built-In innerhalb einer Methode auszuführen, die die Funktion überlädt. Um richtig zu arbeiten, dürfen Sie niemals das eingebaute System überlasten.

diesen Code vor:

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 

und Testbefehl

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

auf die Erklärung auf der Mathworks board gegeben zu erweitern, gebautet funktioniert nur innerhalb einer überladenen Methode, um die Vor- zugreifen bestehende (eingebaute) Methode.

eine Methode in Matlab Überlastung Schatten effektiv die von alles in Umsetzung gebaut mit Ausnahme der Methode, um die Abschattung zu tun, und das Verfahren die Abschattung tun müssen verwenden builtin die in Umsetzung gebaut zuzugreifen statt in sich selbst von Rekursion.

0

In Ihrer aktualisierte Version dieses Problem, wenn Sie t.child(1) aufrufen, wird die subsref Funktionsargument s mit s(1).type='.', s(1).subs='child' und s(2).type='()', s(2).subs='1' erhalten. Die Auswertung dieses Ausdrucks erfolgt nicht in einer Schritt-für-Schritt-Weise, wie in seiner Antwort erwähnt. Daher sollten Sie beim Überschreiben von subsref diese Operationskette behandeln, indem Sie zuerst das '.' Operator. Hier ist ein unvollständiges Beispiel für Ihren Fall,

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 

Sie auch eine detaillierte Beispielcode und Anleitung bei Code Patterns for subsref and subsasgn Methods finden.

Eine weitere Sache, die Sie wissen müssen, ist, dass Methode Mitglieder in dieser Klasse auch über subsref mit '.' Betrieb. Betrachten Sie dieses Thema subsref on classes: how to dispatch methods?, Sie werden feststellen, dass die builtin Funktion keinen Rückgabewert hat (da die aufgerufene Methode keinen Rückgabewert hat). Der Rückgabewert von builtin wird jedoch v zugewiesen (obwohl v durch varargout ersetzt wird), was ein offensichtlicher Fehler ist. Der Autor gibt auch eine temporäre Lösung, indem er try ... catch verwendet, um diesen Fehler zu beheben.

Verwandte Themen