Copy MATLAB object of a handle class - matlab

I've implemented a handle class in MATLAB, and I've tried to copy an object of the class by calling the following two functions:
objByteArray = getByteStreamFromArray(obj);
newObj = getArrayFromByteStream(objByteArray);
But from time to time I get the following error:
Error using getArrayFromByteStream
Unable to read data stream because the data contains a bad version or endian-key
Is there another way to copy an object of a handle class?

Since you're working with a handle class, you can inherit your class from matlab.mixin.Copyable, which will give your class a customizable copy method.
By default, the copy method will implement a shallow copy of the class properties (i.e. if the class properties are themselves handle classes, the copies will be references to the original properties), but you can customize the copy operation to implement a deep copy (i.e. a copy operation is performed on the class properties as well).
Documentation for matlab.mixin.Copyable.

Related

Stop Matlab from using set methods when loading matlab.mixin.Copyable object?

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.

Add a method to Matlab table class

I wish to add a method called nansubset to the table class. Essentially it allows you to call T(r,c) where r and c are real positive integer vectors possibly containing NaN's.
Stubbed code for nansubset.m could be:
function T = nansubset(T, r, c)
T = T(r,c);
end
I am following the instructions here, which detail how to add a new method to the cell class. Basically, in a folder on my Matlab path, I create a folder called #table, and within this folder, create a file called nansubset.m.
I am getting the following problems:
>> tmpT = table(); nansubset(tmpT, 1, 1)
Undefined function 'nansubset' for input arguments of type 'table'.
and
>> doc #table/nansubset
Your search - #table/nansubset - did not match any documents.
However:
edit nansubset
and
edit #table/nansubset
both open the method file in my editor.
Further, I followed the instructions in the above link to add the plus method to the cell class and find that it works perfectly.
Can someone please explain to me how I can add this additional method to the table class?
With the release of Matlab R2012b (version 8), the class folder behavior changed (emphasis is mine):
In MATLAB Versions 5 through 7, class folders do not shadow other class folders having the same name, but residing in later path folders. Instead, the class the combination of methods from all class folders having the same name define the class. This is no longer true.
For backward compatibility, classes defined in class folders always take precedence over functions and scripts having the same name, even those that come before them on the path.
The combination of the two bold statements explains the behavior:
cell is a built-in Matlab function that predates the new OOP rules that returns an instance of its class. And before R2012b, adding methods to a class folder called #cell added the methods to the object returned from the cell function (which isn't defined with a classdef nor a class folder); this ability was retained for compatibility with legacy user code.
table was added after R2012b, is defined via a class folder, and is Sealed. Since it is Sealed, it cannot be subclassed. And with the new rules, any #table folder without an associated classdef file will not register as a class folder nor will its methods be composed into the existing class unless it is part of the legacy system (like cell).
I can see three workarounds listed in the order I think is best:
Have a function on the Matlab path, just like the one you have, and always call the "method" using function notation instead of dot notation. If you need more "methods" group them in a folder or package (if good namespacing is desired) on the path. Since table is a value class, this option doesn't seem bad.
Create a wrapper class like the one below. It is cumbersome but automatically encapsulates the additional functions.
classdef MyTable < handle
properties
tab;
end
methods
function mytab = MyTable(varargin)
mytab.tab = table(varargin{:});
end
function tabnan = nansubset(mytab,r,c)
tabnan = mytab.tab(r,c);
end
end
end
Create a local copy of [matlabroot,'\toolbox\matlab\datatypes\#table\*'] and add the methods directly. I can't think of any huge drawbacks to this per se, but it feels weird copying internals like this.

How can I load a matlab object of which the class definition file was placed in separate package

Is it possible in matlab to load an object of which the class definition file was placed in separate package?
For example:
T = myTestClass;
save('T');
Now I want to place my class in a package, so I create the directory structure as follows:
+myTestPack/#myTestClass/myTestClass.m
Next I try to recover the saved object:
import myTestPack.*
load('T.mat');
The outcome is always:
Warning: Variable 'T' originally saved as a myTestClass cannot be instantiated as an object and will be read in as a uint32.
Is there any way to solve this problem? I would like to restructure my code but a lot of old data was saved as objects.
Maybe I need to add loadobj/saveobj methods to the definition file or maybe there is a way to rename the class from myTestClass to myTestPack.myTestClass?
Thank you for your suggestions!
You need to add loadobj method to your new class. You also need a simple class myTestClass in the old location with just a loadobj method which calls loadobj method of the moved class. MATLAB does not know that you have moved the class. When loading all it knows is that it is of class type myTestClass and tries to create one by looking up myTestClass.

Intersystems caché - programmatically create new class

Is it possible to write ObjectScript method, which will create new class in namespace and compile it? I mean programmatically create new class and store it. If so, can I edit this class using ObjectScript later(and recompile)?
Reason: I have class structure defined in string variable and I need to add new class to namespace according this string.
Nothing is impossible. Everything in Caché can be created programmatically. And, Classes is not a execution. There are at least two ways to do it:
simple SQL Query CREATE TABLE, will create a class.
and as you already mentioned ObjectScript Code, which can do this.
All of definition of any classes defined in other classes. Which you can find in package %Dictionary.
The class itself defined in %Dictionary.ClassDefinition. Which have some properties, for defining any parts of classes. So, this is a simple code which create some class, with one property.
set clsDef=##class(%Dictionary.ClassDefinition).%New()
set clsDef.Name="package.classname"
set clsDef.Super="%Persistent"
set propDef=##class(%Dictionary.PropertyDefinition).%New()
set propDef.Name="SomeProperty"
set propDef.Type="%String"
do clsDef.Properties.Insert(propDef)
do clsDef.%Save()
And in latest versions, there is one more way for create/change class. If you have text of class as you can see it in Studio. Then, you can load it in Caché, with class %Compiler.UDL.TextServices
Yes, it is. You likely want to make use of %Dictionary.ClassDefinition and the related %Dictionary.*Definition classes (especially %Dictionary.PropertyDefinition, %Dictionary.MethodDefinition and %Dictionary.IndexDefinition) to create and/or modify your class. Provided your string contains some reasonable representation of the data, you should be able to create the class this way.
The actual class documentation is available at http://docs.intersystems.com/cache20141/csp/documatic/%25CSP.Documatic.cls?CLASSNAME=%25Dictionary.ClassDefinition
You can then compile the class by calling $system.OBJ.Compile("YourPackage.YourClass","ck").
(Note: If your string contains the exported XML definition of the class, you could also write the XML representation to a stream and then call $system.OBJ.LoadStream() to import the XML definition. I would only recommend this if you have an exported class definition to start with.)

About getting a new NSManagedObject object

I watch the Core Data guides, and there are two way to obtain a new NSManagedObject instances.
- initWithEntity:insertIntoManagedObjectContext: of NSManagedObject class
+ insertnewObjectForEntityForName:inManagedObjectContext: of NSEntityDescription class
Are there any difference between both methods? Or, they just mean the same thing for obtaining a new NSManagedObject under any conditions.
Based on what it's said on the documentation, by using the class method from NSEntityDescription to instantiate the NSManagedObject it's possible to do it without declaring/importing its header. By setting the name of the class you will get back a "fully configured instance" of the object.
It's useful on early stages of development when things are changing constantly but it can be a risk factor since you don't get any compilation errors or warnings if you misspell the name of your class, since it's a string.
The method from NSManagedObject needs that the interface of the specific class imported to your file, and make it more robust against errors, since the compiler can check if that class exist.
For instance they will have the same result, they will return an instance of the specified class. Although the retain counts will be different:
- initWithEntity:insertIntoManagedObjectContext: (retain count == +1)
+ insertnewObjectForEntityForName:inManagedObjectContext: (retain count == 0)
Here it is the documentation
NSEntityDescription Class Reference (insertNewObjectForEntityForName:inManagedObjectContext:)
Return Value
A new, autoreleased, fully configured instance of the class for the entity named entityName. The instance has its entity description set and is inserted it into context.
Discussion
This method makes it easy for you to create instances of a given entity without worrying about the details of managed object creation.
The method is particularly useful on Mac OS X v10.4, as you can use it to create a new managed object without having to know the class used to represent the entity. This is especially beneficial early in the development life-cycle when classes and class names are volatile.
On Mac OS X v10.5 and later and on iOS, you can instead use initWithEntity:insertIntoManagedObjectContext: which returns an instance of the appropriate class for the entity.
NSManagedObject Class Reference (initWithEntity:insertIntoManagedObjectContext:)
Return Value
An initialized instance of the appropriate class for entity.
Discussion
NSManagedObject uses dynamic class generation to support the Objective-C 2 properties feature (see “Declared Properties”) by automatically creating a subclass of the class appropriate for entity.initWithEntity:insertIntoManagedObjectContext: therefore returns an instance of the appropriate class for entity. The dynamically-generated subclass will be based on the class specified by the entity, so specifying a custom class in your model will supersede the class passed to alloc.
If context is not nil, this method invokes [context insertObject:self] (which causes awakeFromInsert to be invoked).
You are discouraged from overriding this method—you should instead override awakeFromInsert and/or awakeFromFetch (if there is logic common to these methods, it should be factored into a third method which is invoked from both). If you do perform custom initialization in this method, you may cause problems with undo and redo operations.
In many applications, there is no need to subsequently assign a newly-created managed object to a particular store—see assignObject:toPersistentStore:. If your application has multiple stores and you do need to assign an object to a specific store, you should not do so in a managed object's initializer method. Such an assignment is controller- not model-level logic.