MATLAB Answers

0

Subclassing Matlab built-in types w/ properties: concise basic operations and flexible superclass

Asked by D. Plotnick on 29 Apr 2019
Latest activity Edited by per isakson
on 5 May 2019
Hello all,
I recently realized that there is considerable inherent power in subclassing Matlab primitives like double or char (edit, char is sealed and cannot be sub-classed). There is a good series of articles here.
Now, I want to create a numeric class that functions just like double, single, int16, etc generally, but with its own custom properties and methods. Let's use the following example
classdef temperature < double
properties
units = 'K';
end
methods
function obj = temperature(data,unit)
if nargin == 0
data = 0;
unit = 'K';
elseif nargin == 1
unit = 'K';
end
obj = obj@double(data);
obj.units = unit;
end
function sref = subsref(obj,s)
switch s(1).type
case '.'
switch s(1).subs
case 'units'
sref = obj.units;
case 'Data'
d = double(obj);
if length(s)<2
sref = d;
elseif length(s)>1 && strcmp(s(2).type,'()')
sref = subsref(d,s(2:end));
end
otherwise
error('Not a supported indexing expression')
end
case '()'
d = double(obj);
newd = subsref(d,s(1:end));
sref = temperature(newd,obj.units);
case '{}'
error('Not a supported indexing expression')
end
end
function obj = subsasgn(obj,s,b)
switch s(1).type
case '.'
switch s(1).subs
case 'units'
obj.units = b;
case 'Data'
if length(s)<2
obj = temperature(b,obj.units);
elseif length(s)>1 && strcmp(s(2).type,'()')
d = double(obj);
newd = subsasgn(d,s(2:end),b);
obj = temperature(newd,obj.units);
end
otherwise
error('Not a supported indexing expression')
end
case '()'
d = double(obj);
newd = subsasgn(d,s(1),b);
obj = temperature(newd,obj.units);
case '{}'
error('Not a supported indexing expression')
end
end
function obj = minus(obj,B)
a = double(obj);
b = double(B);
newd = a - b;
obj = temperature(newd,obj.units);
end
function obj = rdivide(obj,B)
a = double(obj);
b = double(B);
newd = a./b;
obj = temperature(newd,obj.units);
end
function obj = times(obj,B)
a = double(obj);
b = double(B);
newd = a.*b;
obj = temperature(newd,obj.units);
end
function obj = mtimes(obj,B)
a = double(obj);
b = double(B);
newd = a*b;
obj = temperature(newd,obj.units);
end
function obj = mrdivide(obj,B)
a = double(obj);
b = double(B);
newd = a/b;
obj = temperature(newd,obj.units);
end
function obj = plus(obj,B)
a = double(obj);
b = double(B);
newd = a + b;
obj = temperature(newd,obj.units);
end
function obj = farenheit(obj)
switch obj.units
case 'K'
obj = obj*9/5 - 459.67;
obj.units = 'F';
end
end
function obj = kelvin(obj)
switch obj.units
case 'F'
obj = (obj + 459.67)*5/9;
obj.units = 'K';
end
end
end
end
Here, I have set up a class that has an associated 'units' to describe its value (one can imagine additional attached metadata). I have set up the required methods for data access and assignment (subsasgn and subsref) a series of basic math operations (mdivide,plus,times,etc.) that allow me to return an object of the temperature class if I do, for example:
T = temperature(70+rand(10,1),'F');
T2 = T + 5;
This generally works fairly well, but I have two major questions:
  • Is there a way to 'generify' the operation that are methods of the superclass, double? This would be for the basic operations plus, minus, etc. which all follow the same basic form: convert the object to a double, do the operation on the double data, then convert back to the temperature class. It seems like unnecessary code to have to reimplement all of the methods of double (see methods(double.empty) for the whole list) in this manner. Is there a way to simplify this? Nominally something like:
function obj = genop(obj,optype,B,varargin)
if ismethod(double.empty,optype)
a = double(obj);
b = double(B);
d = feval(@(optype),a,b),varargin{:})
obj = temperature(d,obj.units)
end
end
where I can still use e.g. obj2 = obj + 5 and have it return the expected values as a temperature.
  • Is there a way to 'generify' the superclass. Nominally so that I can store the temperature as a double,single, gpuArray,int32, etc. without having to create independent versions of the subclass for each superclass (e.g classdef temperatureS < single, and replacing all of the double commands above by single). This also seems like redundant code, and a recipe for errors as one would need to make sure any changes were replicated across all versions. Would it be better not to subclass double at all, and instead use a data holder property obj.Data and just use cast? e.g.
classdef temperature
properties
data
units
end
% Make subsasgn and subsref so it mimics a numeric array
methods
function obj = plus(obj,B)
c = class(obj.data);
b = cast(B,c);
d = obj.data + b;
obj = temperature(d,obj.units);
end
end
end
but this seems to remove any of the advantages of subclassing a built-in primitive type.
Are there any other obvious tricks/pitfalls to subclassing Matlab built-in classes?
Cheers,
DP

  0 Comments

Sign in to comment.

0 Answers