Can I make MATLAB use a custom copy method when creating an array of my class?
7 visualizzazioni (ultimi 30 giorni)
Mostra commenti meno recenti
Harry Dymond
il 26 Giu 2020
Commentato: Ameer Hamza
il 26 Giu 2020
I'm having an issue with a class that has properties that are handle classes. I've created this example to demonstrate the problem:
classdef Problem_Class < handle
properties (SetObservable = true)
Prop1
end
methods
function s = Problem_Class
s.Prop1 = Prop1_Class;
end
end
end
classdef Prop1_Class < handle
properties (SetObservable = true)
Prop2
end
methods
function s = Prop1_Class
s.Prop2 = Prop2_Class;
end
end
end
classdef Prop2_Class < handle
properties (SetObservable = true)
Name (1,:) char
end
methods
function s = Prop2_Class
s.Name = '';
end
end
end
So, If I create an instance of my Problem_Class:
problem = Problem_Class
The issue arises if I now create an array of Prop1, for example:
problem.Prop1.Prop2.Name = 'index 1';
problem.Prop1(8).Prop2.Name = 'index 8';
problem.Prop1(7).Prop2.Name = 'index 7';
If we look at the .Name property for each problem.Prop1.Prop2, I'd hope to see this:
[problem.Prop1.Prop2]; {ans.Name}'
{'index 1'}
{1×0 char }
{1×0 char }
{1×0 char }
{1×0 char }
{1×0 char }
{'index 7'}
{'index 8'}
But instead I get this:
{'index 1'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 8'}
The reason this is happening is explained here: https://www.mathworks.com/help/matlab/matlab_oop/initializing-arrays-of-handle-objects.html, where it is stated (at the end) "results in two calls to the class constructor. The first creates the object for array element A(4,5). The second creates a default object that MATLAB copies to all remaining empty array elements."
So, when I call problem.Prop1(8).Prop2.Name = 'index 8'; MATLAB calls the Prop1_Class constructor once, to populate problem.Prop1(8), and then a second time to create a Prop1_Class object that it copies to problem.Prop1(2:7). So now problem.Prop1(2:7) all point to the same object in memory. I don't want this! I need problem.Prop1(2:7) to be unique elements, and I need this to happen implicitly rather than having to pre-initialise the problem.Prop1() array.
I thought I had found the solution here: https://www.mathworks.com/help/matlab/matlab_oop/custom-copy-behavior.html, - I created a class based on the HandleCopy example on that page, and made Problem_Class, Prop1_Class, and Prop2_Class all inherit from that, so that all of them should have the custom copy behaviour. I have verified that, e.g. a = copy(b), where "b" is an instance of the Prop1_Class, then behaves as expected, creating an independent copy of b in a (such that a~=b, but their properties have the same values). However, MATLAB does not appear to call the custom copy method when creating an array!
Going back to the previous example: when I call problem.Prop1(8).Prop2.Name = 'index 8'; once MATLAB has created the problem.Prop1(8) element, and then creates the second Prop1_Class object and "copies" that to problem.Prop1(2:7), how do I get MATLAB to use a custom copy function, so I can prevent all problem.Prop1(2:7) pointing at the same object?
Many thanks for reading this post, all help gratefully received!
2 Commenti
Peter O
il 26 Giu 2020
Hi Harry,
Let me start off by saying that MATLAB's OO's is not my strongest suit, and I might be suggesting what you've already tried in different words.
Is it necessary that Prop2 is also a handle class? I can get the desired result if instantiate a new Prop2 object during the copy and override the default copy options. I agree that you should be able to get a "fresh" Prop2 object for each in the fill-in which can retain handle's memory space advantages.
classdef Problem_Class < handle
properties (SetObservable = true)
Prop1
end
methods
function s = Problem_Class
s.Prop1 = Prop1_Class;
end
end
end
classdef Prop1_Class < matlab.mixin.Copyable & handle
properties (SetObservable = true)
Prop2
end
methods
function s = Prop1_Class
s.Prop2 = Prop2_Class;
end
end
methods(Access = protected)
% Override copyElement:
function cpObj = copyElement(obj)
cpObj = copyElement@matlab.mixin.Copyable(obj);
% New Init of Prop2
cpObj.Prop2 = Prop2_Class;
end
end
end
classdef Prop2_Class
properties (SetObservable = true)
Name % (1,:) char
end
methods
function s = Prop2_Class
s.Name = '';
end
end
end
Run
% Test Output
clear all
clear classes
A = Problem_Class;
A.Prop1.Prop2.Name = 'Index 1';
A.Prop1(8).Prop2.Name = 'Index 8';
A.Prop1(7).Prop2.Name = 'Index 7';
[A.Prop1.Prop2]; {ans.Name}'
A.Prop1(7).Prop2.Name = 'index 7, again';
[A.Prop1.Prop2]; {ans.Name}'
Gives
ans =
8×1 cell array
{'Index 1'}
{0×0 char }
{0×0 char }
{0×0 char }
{0×0 char }
{0×0 char }
{'Index 7'}
{'Index 8'}
ans =
8×1 cell array
{'Index 1' }
{0×0 char }
{0×0 char }
{0×0 char }
{0×0 char }
{0×0 char }
{'index 7, again'}
{'Index 8' }
I'll keep chewing on this though. There's either a way to do it, or you've stumbled on a bug or piece of undefined behavior that is deserving of a patch. :)
Risposta accettata
Ameer Hamza
il 26 Giu 2020
It seems like subsasgn() https://www.mathworks.com/help/matlab/ref/subsasgn.html could be helpful in this case. You can control the assignment behavior. The following code shows a very crude example. You can adapt and improve it according to your requirement
classdef Problem_Class < handle
properties (SetObservable = true)
Prop1
end
methods
function s = Problem_Class
s.Prop1 = Prop1_Class;
end
function A = subsasgn(A, S, B)
n = S(2).subs{:};
m = numel(A.Prop1);
num_new_elem = n-m;
if num_new_elem > 1
for i=1:num_new_elem-1
A.Prop1(m+i).Prop2.Name = '';
end
A.Prop1(m+num_new_elem).Prop2.Name = B;
else
A.Prop1(n).Prop2.Name = B;
end
end
end
end
Example
problem = Problem_Class;
problem.Prop1(1).Prop2.Name = 'index 1'; % the current definition of subsasgn requires to use index (1) for first element too
problem.Prop1(8).Prop2.Name = 'index 8';
problem.Prop1(5).Prop2.Name = 'index 7';
x = [problem.Prop1.Prop2];
{x.Name}'
Output
ans =
8×1 cell array
{'index 1'}
{1×0 char }
{1×0 char }
{1×0 char }
{'index 7'}
{1×0 char }
{1×0 char }
{'index 8'}
2 Commenti
Ameer Hamza
il 26 Giu 2020
Yes, the example in my answer was very crude. For a general case, several other things might need to be considered. Hopefully, a workaround would be added in a future release.
Più risposte (0)
Vedere anche
Categorie
Scopri di più su Matrix Indexing in Help Center e File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!