I have some big handle objects hierarchy, for example
classdef A < handle
b;
c;
end
classdef B < handle
d;
e;
end
classdef C < handle
f;
g;
end
a = A;
where a,b,c,d,e,f are all instances of their respective classes. Eventually, there are basic data types (vectors or scalars of, say, double).
I need the save of a to hold all the information about a,b,c,d,e,f, in a way that can be restored later.
Runtime is not an issue for me right now.
Development time is an issue.
How can I easily create a save of the top A type object to disk? (a deep copy, to disk).
I want it to happen automatically (built in), somewhat like java's serialize maybe, without me having to change some save method for every change I make in the classes' structure.
When saving objects with MATLAB using the built-in save, it will automatically save a "deep-copy" in that it saves all properties of the object and if any of those properties are also objects or arrays of object, they will themselves be serialized. The objects can then easily be loaded again using the built-in load function. It is worth noting that in order for an object of a custom class to be loaded properly, the class definition must be on the path when calling load.
save('output.mat', 'a')
fromdisk = load('output.mat');
If you want to customize the saving or loading functionality of your class, you can overload the saveobj and loadobj methods, respectively.
Related
I have a classe derived from matlab.mixin.Copyable. Some of the properties have set methods. I want the setting of those properties to also update other properties so that the relationship between the parameters are consistent (a classic use of set methods).
Unfortunately, when I load a *.mat file [specifically using, say, x=load('file.mat')], setters are also used. There should be no need for that kind of automatic update of multiple parameters, since all the object's properties can be copied from the *.mat file and self-consistency is automatically maintained. Instead, using setters during load causes errors because the setter uses other properties that haven't yet been assigned to in the load process. I see this from error that occurs during the load, and from checking the properties that are needed by the setter.
Is there any way to force the load to do a simple replication of the property values contained the *.mat file? Some of the properties are complex objects themselves, so what's needed is a recursive copy-by-value during load. It seems rather inappropriate to use setters during a load, for the reasons above.
P.S. I say above that the setter uses another as-yet-unassigned property. Let's call this property p2. It also gets assigned to by a setter for 3rd property s1. It seemed odd, but s1 does have a value, while p2 does not. One possible reason is that p2 relies on other properties in addition to s1, and those might not have been assigned to when s1 is loaded (i.e., when the s1 setter is invoked). The whole problem stems from the fact that load occurs outside of the context and the order in which properties are assigned to during the execution of the code that created it. This is the key reason why (it seems to me that) load should not use setters. Otherwise, it seems to be incompatible with either copying or loading (I'm not sure which at the moment -- maybe both).
The process MATLAB uses to load objects is well documented. This page mentions, among many other things, that the set methods are called to prevent issues that happen when a class definition changes, and you try to load an object from that class from an old file.
There is also a page explaining how to modify the save and load process. This page describes the use of loadobj, defined as a static method to your class, to change how the object is constructed from the information in the file. loadobj is called when one of the set methods throws an error; but it is always called if saveobj returns a struct. That is, one solution to your problem is to have saveobj create a struct with all the data, and have loadobj reconstruct your object from that struct.
This other documentation page describes how to design your class to avoid property initialization order dependency -- which is the problem you describe having. The way to do this is to have all public properties be dependent properties, with the "actual" data properties being hidden and not interdependent. You might be able to construct your class this way, for example with a single data property (a cell array or struct), which would be loaded and saved in one go, and the public properties simply using a portion of this cell or struct in the set and get methods.
In my example, I have a class representing a certain set of data. Some of the data's properties are written to the class' properties, while the class' methods mostly perform tasks like reading the raw data from spreadsheets and doing some pre-calculations.
I was wondering if the following is according to the "pure doctrine" of OOP, and if not, if and how it could be syntactically improved.
Here is a very simple (untested) implementation of such a class.
classdef dataObjectTest < handle
properties
filename char
pathFilename char
rawData double
end
methods
function obj = setPathnameFromFilename(obj, filename)
%setPathnameFromFilename Determine full path- & filename from single filename
% filename: Name of the file containing the raw data, e.g., 'Test.xls'
obj.filename = filename;
intermed = dir(obj.filename);
obj.pathFilename = fullfile(intermed.folder, obj.filename);
end
function obj = loadRawDataFromSpreadsheet(obj)
% loadRawDataFromSpreadsheet Convert the raw data stored in a spreadsheet into a Matlab
% array, using the full path- & filename determined by setPathnameFromFilename
% rawData: Array contating the raw data
obj.rawData = xlsread(obj.pathFilename);
end
end
end
And here is how one would call this class from another program/script:
test = dataObjectTest;
test.setPathnameFromFilename('Test.xls');
test.loadRawDataFromSpreadsheet;
I am especially concerned about the third line. Since both input and output arguments of the method loadRawDataFromSpreadsheet are properties of the class, it is unnecessary to explicitly define them in the function header. On the other hand, this implementation seems somehow awkward to me and not in "spirit" of OOP, which is a lot about clearly defined interfaces between the user and the class.
So although my code works, I'm still wondering if this is "the right way" to do it or if could be significantly improved.
UPDATE: Admittedly, the choice of my methods' names may have been misleading, but this was not the point of the question. So let me try to clarify.
The first method takes the user-given string filename and returns pathFilename. But since this is also a property of the class, it is not visible from the function signature, i.e., the function should look like:
function pathFilename = setPathnameFromFilename(obj, filename)
But this doesn't work in Matlab. (The varName = part corresponds to return varName in other languages.)
The second method returns the array rawData, and since the input argument pathFilenameis, again, a property of the class, neither are visible in the method's signature.
So my concern was about having methods which actually have in- and output arguments, but do not reveal them through their signature.
It's basically fine, but I'd implement it like this:
classdef dataObjectTest < handle
properties
filename char
pathname char
rawData array
end
methods
function loadRawDataFromSpreadsheet(obj, filename)
obj.filename = filename;
obj.pathname = dataObjectTest.extractPathName(filename
obj.rawData = xlsread(obj.pathname);
end
end
methods (Static, Access = private)
function pathname = extractPathName(filename)
intermed = dir(filename);
pathname = fullfile(intermed.folder, filename);
end
end
end
Notes:
Since it's a handle object, you don't need obj as an output argument of the main method.
I've moved the extraction of the path to a private static method, as it's really a utility function (you could instead implement as an actual subfunction rather than a method).
I've renamed the main method to start with load rather than get, to make sure it isn't mistaken for a property get method.
I wouldn't worry too much about the "spirit of OOP" - instead, just make sure your code works, is well-organised, is testable, and maintainable.
It is just fine according to the "doctrine" of pure OOP for a method to have no in or out parameters apart from the object reference.
However, what you are doing doesn't conform to normal practice for method naming. A method with a name starting with get is normally a "getter". The primary purpose of a "getter" is to return some component of the class. Generally speaking, a "getter" should not modify the target object.
But you have two get... methods that 1) modify the target object and 2) don't return anything.
In my opinion:
the first one should be named setPathnameFromFilename or maybe just setFilename.
the second one should be named loadRawDataFromSpreadsheet or something like that.
On the other hand, this implementation seems somehow awkward to me and not in "spirit" of OOP, which is a lot about clearly defined interfaces between the user and the class.
I actually don't see that at all. Sure, the interface is not clearly defined, but that is largely because you have not documented the methods and because you have chosen (IMO) misleading / non-informative class and method names.
(Disclaimer: I am a Java programmer. However, the basic principles of OOP are largely the same for all OO languages.)
I defined a class in one of my many MATLAB packages. To my suprise, I could not access a constant property of my class without importing the class definition. Even if it is a method of the class itself. Like so:
classdef TestClass
properties( Constant )
c=0;
end
methods
function obj = TestClass()
end
function getC(obj)
import test.TestClass;
disp(TestClass.c);
end
end
end
I just want to check whether I am doing something wrong here or is this the correct way to use constants in MATLAB.
Since you have placed TestClass inside a package Matlab needs to know where to look to find the definition for this class, even if it's a reference from within the class or function. An alternate to the above code could be:
function getC(obj)
disp(test.TestClass.c);
end
Alternately, if within a class, constant values can be accessed from the object itself.
function getC(obj)
disp(obj.c);
end
If neither of these is working for you, you may need to refresh the classdef for TestClass from memory. This will cause matlab to reload the constant value, which is pulled into Matlab when it first parses the classdef file to determine the structure of the class. This can be done using clear classes, however a warning that it will also clear all other classes, variables, and any breakpoints you have set.
If you want to see if this is necessary you can view the metaclass object to determine what Matlab "thinks" your class structure should be. You can do this using the following.
mc = ?test.TestClass;
mc.PropertyList
You may need to index into the property list to find the specific property you are interested in, but the thing you are looking for are the following fields.
Name: 'c'
Constant: 1
DefaultValue: 0
Data tends to survive longer than code. So I want to save my objects as struct.
The idea is to overwrite the saveobj method calling struct recursively on itself.
classdef foo < handle
properties
Data = 12
end
methods
function data = saveobj(o)
data = struct(o);
end
end
end
I now call:
>> bar = foo;
>> save('test', 'bar')
>> bar2 = load('test')
>> class(bar2.bar)
'foo'
However the saved object is still of type foo. Any help figuring out where I screwed up would be appreciated. Thanks
In case it matters I'm using R2012a on RHLE6
The "problem" is that despite the documentation implying that the output from saveobj gets saved with no knowledge that it originated from an object, that is not the case. The structure knows that it came from an object, and when loaded will get passed to a loadobj method if it exists, or the constructor if it doesn't.
One way to overcome this is to overload save, and then call save on the struct within the save method of the object; another is to have a method that returns a struct and then call save on that struct. There are probably also some hacks that can be done to make it a little more automatic, which is most likely what you really want.
I found a truly ugly hack-around, which might help somebody. Due to its ugliness I will however still wait for an answer.
As Phil already pointed out is the class name still stored with the file. However we can trick matlab with an other file with the same name.
My original class looks like this:
classdef foo < handle
%// This is the original class used to some stuff and it holds
%// important data. But I lost it over time...
properties
Data = 12
end
methods
function data = saveobj(o)
for f = properties(o)'
data.(f{1}) = saveobj(o.(f{1}));
end
end
end
end
Now, since I assume I don't have the original source anymore I can use a dummy class to load the data.
classdef foo < handle
%// This is the pretender class source. Since I lost the source of
%// the original class.
methods (Static)
function data = loadobj(data)
global DATA
DATA = data;
end
end
end
The load will naturally fail, but I can extract the struct feed into the loadobj method by calling:
global DATA
As I said a very ugly solution. That's why I'll wait till somebody smart comes around and finds a better way to solve this problem, but I thought I post it in case it helps somebody.
Another hack-around might be to save the class-code while saving the object.
For loading the object of your class, you could first load the code, store it as a m-file and then load the object of the class.
UPDATE:
This is a known bug - link requires a login to Mathworks to access it.
Summary of bug report
An instance of a MATLAB user-defined
class saved to a MAT file using
Version 7.6 (R2008a) might not load
properly if one of its property values
is an instance of a different MATLAB
class.
Briefly, Mathworks reports that a previously saved top level custom object may be loaded incorrectly (as described below) and that the error occurs on the SAVE step. So, the data is corrupted inside the MAT file.
From my experience this seems to be intermittent. In one data analysis application I wrote out of 75 MAT files 37 were saved with this corruption :(
Be careful with user defined objects. I added the following test on save to make sure that the data is not corrupted
save('MAT_file_name.mat');
tmp=load('MAT_file_name.mat');
if ~isa(tmp.bb,'superClass')
msgbox({'THE FILE WAS NOT SAVED PROPERLY', ...
' ', ...
[' MAT_file_name.mat',]})
end
Orginal Question
Here I am using MATLAB 2008a. This subtle bug is fixed in MATLAB-2009a.
Anyway, the way my two classes are defined, the save/load cycle causes the variables of one class (superClass) to be loaded as variables of my second class (propClass).
Example MATLAB (r2008a) Session
>> bb=superClass;
>> whos
Name Size Bytes Class Attributes
bb 1x1 60 superClass
>> save
>> clear
>> clear classes
>> load
>> whos
Name Size Bytes Class Attributes
bb 1x1 60 propClass
After loading matlab.mat, the variable bb has mysteriously changed from superClass to propClass
Class: superClass
This class needs to contain an array of type propClass and here is it's Naive definition
classdef superClass<handle
properties(SetAccess=private)
a = propClass.empty % need to set this property as type propClass
% otherwise the addProp method throws an error
count=0;
end
methods
function self=superClass
%empty class definitionS
end
function addProp(self)
p = propClass;
self.count = self.count+1;
self.a(self.count)=p;
end
end
end
Class: propClass
PropClass is a second class used by the super class. Its definition is not important for this bug.
Question
So, why is superClass being changed to propClass after the load operation in MATLAB-R2008a? Secondly, how can I change the definition of superClass to avoid this symptom?
Note
I ran into this symptom in a larger class I had written and narrowed down the source of the problem. I know it occurs in the MATLAB session described above but it seems that if I add an object to the property array in superClass then the problem disappears.
So, if I call superClass.addProp before saving then the strange change from superClass to propClass doesn't occur.
That's a strange problem! I haven't come across anything like that, but one thing you could try first is to move the initializations of the properties to the constructor:
classdef superClass < handle
properties (SetAccess = private)
a
count
end
methods
function self = superClass
self.a = propClass.empty;
self.count = 0;
end
function addProp(self)
p = propClass;
self.count = self.count+1;
self.a(self.count) = p;
end
end
end
I'm not sure it will have any effect, but that's the only thing I can think of to try. Hope it helps! =)