OOP load update

View: New views
18 Messages — Rating Filter:   Alert me  

OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Attached is a fix so that the load command works with legacy objects.  
It works for binary and hdf5 formats, but the save command doesn't work
with ASCII files, so I haven't fixed the load command.  Will work on
that later.

This is the last major issue with legacy objects.  There are scads of
minor issues left.

Bob
--
Robert T. Short, Ph.D.
PhaseLocked Systems

# HG changeset patch
# User Robert T. Short <octave@...>
# Date 1241544631 25200
# Node ID 91378b29a535fb93b4a3503b48fcf4b9c57603cd
# Parent  742cf6388a8f1d2c84b580535be09cfce92ac5db
        * file-ops.h (tail()) New function.
        * ov-class.h (reconstruct_parents): New function.
        * ov-class.cc (added reconstruct_parents method): Fix parent list
          when a load is performed.
          (load_hdf5, load_binary): Fix parent list when a load is performed.
          (get_current_method_class): Cleaned up method class extraction.
        * ov-base.h (find_parent_class): Removed pointless FIXME.
        * Associated changelog entries.

diff -r 742cf6388a8f -r 91378b29a535 liboctave/ChangeLog
--- a/liboctave/ChangeLog Sat May 02 07:20:35 2009 -0700
+++ b/liboctave/ChangeLog Tue May 05 10:30:31 2009 -0700
@@ -1,3 +1,7 @@
+2009-05-04  Robert T. Short  <octave@...>
+
+ * file-ops.h (tail()) New function.
+
 2009-04-16  Jaroslav Hajek  <highegg@...>
 
  * idx-vector.cc (idx_vector::idx_range_rep::idx_range_rep (const
diff -r 742cf6388a8f -r 91378b29a535 liboctave/file-ops.h
--- a/liboctave/file-ops.h Sat May 02 07:20:35 2009 -0700
+++ b/liboctave/file-ops.h Tue May 05 10:30:31 2009 -0700
@@ -109,6 +109,18 @@
     return static_members::dir_sep_chars ();
   }
 
+  // Return the tail member of a path.
+  static std::string tail (std::string path)
+  {
+    size_t ipos = path.find_last_of (dir_sep_char ());
+
+    if (ipos != std::string::npos)
+      ipos++;
+    else
+      ipos = 0;
+    return path.substr (ipos);
+  }
+
 private:
 
   // Use a singleton class for these data members instead of just
diff -r 742cf6388a8f -r 91378b29a535 src/ChangeLog
--- a/src/ChangeLog Sat May 02 07:20:35 2009 -0700
+++ b/src/ChangeLog Tue May 05 10:30:31 2009 -0700
@@ -1,3 +1,12 @@
+b2009-05-04  Robert T. Short  <octave@...>
+
+        * ov-class.h (reconstruct_parents): New function.
+        * ov-class.cc (added reconstruct_parents method): Fix parent list
+          when a load is performed.
+          (load_hdf5, load_binary): Fix parent list when a load is performed.
+          (get_current_method_class): Cleaned up method class extraction.
+        * ov-base.h (find_parent_class): Removed pointless FIXME.
+
 2009-05-01  John W. Eaton  <jwe@...>
 
  * error.cc (Vlast_error_file, Vlast_error_name, Vlast_error_line,
diff -r 742cf6388a8f -r 91378b29a535 src/ov-base.h
--- a/src/ov-base.h Sat May 02 07:20:35 2009 -0700
+++ b/src/ov-base.h Tue May 05 10:30:31 2009 -0700
@@ -458,7 +458,6 @@
 
   virtual string_vector parent_class_names (void) const;
 
-  // FIXME -- should this warn if called for a non-class type?
   virtual octave_base_value *find_parent_class (const std::string&)
     { return 0; }
 
diff -r 742cf6388a8f -r 91378b29a535 src/ov-class.cc
--- a/src/ov-class.cc Sat May 02 07:20:35 2009 -0700
+++ b/src/ov-class.cc Tue May 05 10:30:31 2009 -0700
@@ -33,6 +33,7 @@
 #include "Cell.h"
 #include "defun.h"
 #include "error.h"
+#include "file-ops.h"
 #include "gripes.h"
 #include "load-path.h"
 #include "ls-hdf5.h"
@@ -98,15 +99,13 @@
 {
   std::string retval;
 
-  // FIXME -- is there a better way to do this?
   octave_function *fcn = octave_call_stack::current ();
 
   std::string my_dir = fcn->dir_name ();
 
-  size_t ipos = my_dir.find_last_of ("@");
+  std::string method_class = file_ops::tail (my_dir);
 
-  if (ipos != std::string::npos)
-    retval = my_dir.substr (ipos+1);
+  retval = method_class.substr (1);
 
   return retval;
 }
@@ -814,6 +813,65 @@
     }
 }
 
+//  Load/save does not provide enough information to reconstruct the
+//  class inheritance structure.  reconstruct_parents () attempts to
+//  do so.  If successful, a "true" value is returned.
+//
+//  Note that we don't check the loaded object structure against the
+//  class structure here so the user's loadobj method has a chance
+//  to do its magic.
+bool
+octave_class::reconstruct_parents (void)
+{
+  bool retval = true, might_have_inheritance = false;
+  std::string dbgstr = "dork";
+
+  // First, check to see if there might be an issue with inheritance.
+  for (Octave_map::const_iterator p = map.begin (); p != map.end (); p++)
+    {
+      std::string  key = map.key (p);
+      Cell         val = map.contents (p);
+      if ( val(0).is_object() )
+ {
+  dbgstr = "blork";
+  if( key == val(0).class_name() )
+    {
+      might_have_inheritance = true;
+      dbgstr = "cork";
+      break;
+    }
+ }
+    }
+  
+  if (might_have_inheritance)
+    {
+      octave_class::exemplar_const_iterator it
+ = octave_class::exemplar_map.find (c_name);
+
+      if (it == octave_class::exemplar_map.end ())
+ retval = false;
+      else
+ {
+  octave_class::exemplar_info exmplr = it->second;
+  parent_list = exmplr.parents ();
+  for (std::list<std::string>::iterator pit = parent_list.begin ();
+       pit != parent_list.end ();
+       pit++)
+    {
+      dbgstr = *pit;
+      bool dbgbool = map.contains (*pit);
+      if (!dbgbool)
+ {
+  retval = false;
+  break;
+ }
+    }
+ }
+    }
+
+  return retval;
+}
+
 bool
 octave_class::save_ascii (std::ostream& os)
 {
@@ -1033,15 +1091,20 @@
  {
   map = m;
 
-  if (load_path::find_method (class_name(), "loadobj") != std::string())
+  if (!reconstruct_parents ())
+    error("unable to reconstruct object inheritance");
+  else
     {
-      octave_value in = new octave_class (*this);
-      octave_value_list tmp = feval ("loadobj", in, 1);
+      if (load_path::find_method (c_name, "loadobj") != std::string())
+ {
+  octave_value in = new octave_class (*this);
+  octave_value_list tmp = feval ("loadobj", in, 1);
 
-      if (! error_state)
- map = tmp(0).map_value ();
-      else
- success = false;
+  if (! error_state)
+    map = tmp(0).map_value ();
+  else
+    success = false;
+ }
     }
  }
       else
@@ -1253,21 +1316,26 @@
     {
       map = m;
 
-      if (load_path::find_method (class_name(), "loadobj") != std::string())
+      if (!reconstruct_parents ())
+ error("unable to reconstruct object inheritance");
+      else
  {
-  octave_value in = new octave_class (*this);
-  octave_value_list tmp = feval ("loadobj", in, 1);
+  if (load_path::find_method (c_name, "loadobj") != std::string())
+    {
+      octave_value in = new octave_class (*this);
+      octave_value_list tmp = feval ("loadobj", in, 1);
 
-  if (! error_state)
-    {
-      map = tmp(0).map_value ();
-      retval = true;
+      if (! error_state)
+ {
+  map = tmp(0).map_value ();
+  retval = true;
+ }
+      else
+ retval = false;
     }
   else
-    retval = false;
+    retval = true;
  }
-      else
- retval = true;
     }
   
  error_cleanup:
diff -r 742cf6388a8f -r 91378b29a535 src/ov-class.h
--- a/src/ov-class.h Sat May 02 07:20:35 2009 -0700
+++ b/src/ov-class.h Tue May 05 10:30:31 2009 -0700
@@ -144,6 +144,8 @@
   void print_with_name (std::ostream& os, const std::string& name,
  bool print_padding = true) const;
 
+  bool reconstruct_parents (void);
+
   bool save_ascii (std::ostream& os);
 
   bool load_ascii (std::istream& is);

OOP load update

by John W. Eaton-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On  5-May-2009, Robert T. Short wrote:

| Attached is a fix so that the load command works with legacy objects.  
| It works for binary and hdf5 formats, but the save command doesn't work
| with ASCII files, so I haven't fixed the load command.  Will work on
| that later.

How does it fail?  It seems to work for me, at least with the simple
object I tried to save.  Note that it can't work for "save -ascii",
but that's not what the save_ascii function is for.  It is called when
saving objects in the "-text" format.  The naming is an unfortunate
historical artifact...

I would guess that the changes to handle loading from a file saved in
Octave's -text file format should be about the same as for the binary
and hdf5 formats.

What about loading from the MAT file format?  Is that working now?

| This is the last major issue with legacy objects.  There are scads of
| minor issues left.

I checked in this changeset with a few more minor changes.

Thanks,

jwe

Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Ahh!  I did try save --ascii.  My bad.  Should have read the docs more
carefully.  Actually I didn't really test this on any real variety of
file formats.  Just one more item.....  In fact when I said binary, I
was being very sloppy.  I should have said matfile version 6.  I saved
v6 mat files in MATLAB and read them here as well as saving them from
octave and reading them back.  So, Bob needs to do some more testing.  I
think the structure is pretty solid though.  There are some interesting
issues that I will bring up when I can spare a few minutes.

It works very well from hdf5 and mat file formats and, yes, the changes
will be essentially the same for the save_ascii function.   I tested
this on many different types of inheritance and aggregation and it works
really well.  I didn't have to muck about your "exemplar" stuff at all
either, so that was well-conceived.

Note that if there is an object load failure I don't return the struct,
just fail.  I will try to figure out something that works.  It might be
just fine to return the broken object and use the struct(obj) function
to get the struct info from the class.  Of course, I will have to write
the struct(obj) function, but I think that will be pretty easy.

I will fix the save_ascii thing and try to figure out what to do when
there is a class structure mismatch.  The next thing is to see that
"clear classes" works.  Since I have to figure out exactly what "works"
means, there is some effort involved.

Just some other information.

I went and re-read the MATLAB documentation; it says very clearly that a
class constructor must be able to handle the no-argument case since the
constructor is called for loading from a mat file and and building
arrays of objects.  They don't actually test for it of course, and I am
quite sure they DON'T call the constructor for loading from a mat file.  
I think they do for creating an array of objects.  Some experimentation
here and I will do the correct thing.  I want to do a lot of the little
detail stuff first and will get back to this issue later.

Also, in the MATLAB documentation they say that field names are always
32 bits long, but that is NOT what is actually saved in the mat file.  
It saves names in the minimum space required.

Bob
--
Robert T. Short
PhaseLocked Systems



John W. Eaton wrote:

> On  5-May-2009, Robert T. Short wrote:
>
> | Attached is a fix so that the load command works with legacy objects.  
> | It works for binary and hdf5 formats, but the save command doesn't work
> | with ASCII files, so I haven't fixed the load command.  Will work on
> | that later.
>
> How does it fail?  It seems to work for me, at least with the simple
> object I tried to save.  Note that it can't work for "save -ascii",
> but that's not what the save_ascii function is for.  It is called when
> saving objects in the "-text" format.  The naming is an unfortunate
> historical artifact...
>
> I would guess that the changes to handle loading from a file saved in
> Octave's -text file format should be about the same as for the binary
> and hdf5 formats.
>
> What about loading from the MAT file format?  Is that working now?
>
> | This is the last major issue with legacy objects.  There are scads of
> | minor issues left.
>
> I checked in this changeset with a few more minor changes.
>
> Thanks,
>
> jwe
>
>
>  


Re: OOP load update

by WMennerich :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hello,
Matlab object loading using mat-files works like this:
The zero-args-case in the constructor is only needed, if you want to load objects which class is not 'defined' within the current matlab session. 'Defined class' means here, that you have at least crated one object of the class, using the constructor with the NON-zero-args-case.

The class stays 'defined', even if you delete all its objects.

If you run 'clear classes', the knowledge about the class is away and you are no more able to load an object using a matfile if the constructor for that class has not a 'zero-args-case'.

Matlab cares only about the first tier of fields of the classes because the content of these fields is not fixed. That means that

a.b=0
a.c=0

and


d.b.f=0
d.c=0

are two structures 'a' and 'd' which give the 'same' class in matlab

Regards, Wolfgang

Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hello Wofgang,

Thanks for this information.  MATLAB actually can reconstruct all of the
fields of the underlying struct from the mat file, but it is not able to
reconstruct inheritance from the mat file.  Calling the constructor
seems like a natural way to do it, but MATLAB doesn't seem to ever call
the constructor when loading from a mat file.

In order to load a class with parent classes, you first need to
instantiate an object from the class constructor and then load the mat
file.  This is now the way things work in octave, but I have been
thinking about invoking the class constructor to fill in the class
structure.  As long as the constructor doesn't require arguments it
should work, but I haven't really thought it out that far yet.

Thanks again.

Bob
--
Robert T. Short
PhaseLocked Systems

WMennerich wrote:

> Hello,
> Matlab object loading using mat-files works like this:
> The zero-args-case in the constructor is only needed, if you want to load
> objects which class is not 'defined' within the current matlab session.
> 'Defined class' means here, that you have at least crated one object of the
> class, using the constructor with the NON-zero-args-case.
>
> The class stays 'defined', even if you delete all its objects.
>
> If you run 'clear classes', the knowledge about the class is away and you
> are no more able to load an object using a matfile if the constructor for
> that class has not a 'zero-args-case'.
>
> Matlab cares only about the first tier of fields of the classes because the
> content of these fields is not fixed. That means that
>
> a.b=0
> a.c=0
>
> and
>
>
> d.b.f=0
> d.c=0
>
> are two structures 'a' and 'd' which give the 'same' class in matlab
>
> Regards, Wolfgang
>
>  


Re: OOP load update

by John W. Eaton-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On  5-May-2009, Robert T. Short wrote:

| MATLAB actually can reconstruct all of the
| fields of the underlying struct from the mat file, but it is not able to
| reconstruct inheritance from the mat file.  Calling the constructor
| seems like a natural way to do it, but MATLAB doesn't seem to ever call
| the constructor when loading from a mat file.

Are you sure about that?  Try creating and saving an object of a class
then make the constructor for that class unavailable in smoe way
(rename the @NAME directory that contains the constructor, for
example).  Then restart Matlab and try loading the file.  Does it
complain about not being able to find the constructor?  Or that it
can't create the class, so it is converting it to a structure instead?

| In order to load a class with parent classes, you first need to
| instantiate an object from the class constructor and then load the mat
| file.  This is now the way things work in octave, but I have been
| thinking about invoking the class constructor to fill in the class
| structure.  As long as the constructor doesn't require arguments it
| should work, but I haven't really thought it out that far yet.

It should not be necessary to call the constructor unless an object of
the class has not yet been created at the point where you need to
know something about the class layout.

jwe

Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

John W. Eaton wrote:

> On  5-May-2009, Robert T. Short wrote:
>
> | MATLAB actually can reconstruct all of the
> | fields of the underlying struct from the mat file, but it is not able to
> | reconstruct inheritance from the mat file.  Calling the constructor
> | seems like a natural way to do it, but MATLAB doesn't seem to ever call
> | the constructor when loading from a mat file.
>
> Are you sure about that?  Try creating and saving an object of a class
> then make the constructor for that class unavailable in smoe way
> (rename the @NAME directory that contains the constructor, for
> example).  Then restart Matlab and try loading the file.  Does it
> complain about not being able to find the constructor?  Or that it
> can't create the class, so it is converting it to a structure instead?
>  
Not 100%, but reasonably.  I haven't tried what you said, but I did (a)
put a breakpoint in the constructor and (b) put some output statement in
the constructor, and (c) making a constructor that failed without an
argument.  None of these things happen.  I have thought of some more
conclusive experiments and will try them.

I need to experiment more just to try to characterize this thing.  Right
now octave works pretty much just like MATLAB.  If you have a class
FooBar that has inheritance and do

 > fubar=FooBar()
 > save foobarfile fubar
 > exit

(start {MATLAB,octave} again)
 > load foobarfile
error:  (some error message about not being able to do this)

but if you do
 > FooBar()
 > load foobarfile

everything works.


> | In order to load a class with parent classes, you first need to
> | instantiate an object from the class constructor and then load the mat
> | file.  This is now the way things work in octave, but I have been
> | thinking about invoking the class constructor to fill in the class
> | structure.  As long as the constructor doesn't require arguments it
> | should work, but I haven't really thought it out that far yet.
>
> It should not be necessary to call the constructor unless an object of
> the class has not yet been created at the point where you need to
> know something about the class layout.
>  
See the example above for what I meant.  If you save a class in a file,
exit {MATLAB,octave} you have to instantiate an object before the load
will work.  If I called the class constructor it would work fine, but if
the constructor HAD to have an argument, it would fail.  Then you would
still have to instantiate an object.  On the other hand, if there is
already an entry in the examplar table, there is no reason to do any of
the above.  I think that is what I do - This makes me think that I had
best have another look, but I think this is what I do.

I think this is really quite a good approach and will probably play with
this.

Bob
--
Robert T. Short, Ph.D.
PhaseLocked Systems

Re: OOP load update

by John W. Eaton-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On  5-May-2009, Robert T. Short wrote:

| I need to experiment more just to try to characterize this thing.  Right
| now octave works pretty much just like MATLAB.  If you have a class
| FooBar that has inheritance and do
|
|  > fubar=FooBar()
|  > save foobarfile fubar
|  > exit
|
| (start {MATLAB,octave} again)
|  > load foobarfile
| error:  (some error message about not being able to do this)
|
| but if you do
|  > FooBar()
|  > load foobarfile
|
| everything works.

What version of Matlab do you have?

jwe

Re: OOP load update

by Judd Storrs :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I'm pretty sure the constructor is called if Matlab doesn't have a class definition. Suppose:

@FooBar/FooBar.m:
function obj = FooBar()
     disp('Hello!');
     obj.data = 'Baz';
     obj = class(obj, 'FooBar');

Then at least in Matlab R14 (7.0.1) (I've edited whitespace):

Matlab Session 1:
>> obj1 = FooBar()
Hello!
obj1 = FooBar object: 1-by-1
>> save('test.mat','obj1')
>> obj2 = FooBar()        
Hello!
obj2 = FooBar object: 1-by-1
>> save('test2.mat','obj2')
>> exit

Matlab Session 2:
>> load test.mat
Hello!
>> load test2.mat
>>

As you see in the second session, matlab does execute the constructor the first time it encounters the FooBar class, but once it knews the class structure, it didn't need to call the constructor again.

--judd


Re: OOP load update

by Judd Storrs :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Tue, May 5, 2009 at 4:57 PM, WMennerich <W.Mennerich@...> wrote:
Matlab cares only about the first tier of fields of the classes because the
content of these fields is not fixed. That means that

a.b=0
a.c=0

and


d.b.f=0
d.c=0

are two structures 'a' and 'd' which give the 'same' class in matlab

I confirm this observation. This underscores the idea that matlab classes only track field names of the class struct members. The same member could be a cell or a struct or a matrix in different objects of the same class simultaneously.

--judd

Parent Message unknown Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hmmm.  Seems that Judd is correct.  If the constructor is available and
if it has no parameters, then the load works even if MATLAB doesn't have
a current class structure.

Judd do you remember the attached emails?   The context was this: I had
suggested that I should call the class constructor, John asked the
question "what about constructors with parameters" so I went and played
with it.  I need to remember what I did, but if you don't do things just
right you get a nastygram like

Warning: Could not determine the fields for class Foo by calling the
         constructor with no input arguments.  The object a has been
         converted to a structure.  To eliminate the conversion create
         an object of class Foo before calling LOAD.

So, what were the conditions under which you got the message?



Whatever, I think this is pretty easy to fix and was already in the
plan.  Basically the load algorithm goes like this

if there might be an inheritance issue then
  if there is no entry in the exemplar table then
     execute the class constructor to create an exemplar table entry
     if executing the class constructor failed, return the broken class

  use the exemplar table to reconstruct the inheritance
  if there is an inconsistency in the inheritance structure return the
broken class

if there is no inheritance problem
  execute the user's loadobj method
  check the class for consistency with the exemplar table entry
  if inconsistent, return the broken class
  else return the class

The only difference between the above and what is done now is that I
don't currently execute the class constructor.



MATLAB returns a struct, not the broken class but as of now I don't see
how to return a struct without some kludge.  There is really no
difference between the broken class and the correct class except a
warning message and the fact that methods and inheritance may not work
properly.  Once the struct(obj) function works properly the struct can
be obtained from the broken class easily.  This isn't totally MATLAB
compatible, but not bad, and fixing broken classes isn't something that
is a normal activity.  The loadobj function is really designed to patch
up inconsistencies between saved classes and the current class
definitions.   I don't see any facilities in MATLAB for the loadobj
function to fix inheritance issues, but I could make something like that
in octave.

There are actually several items in the last set of emails that I need
to absorb.  Hopefully this weekend....

Bob
--
Robert T. Short
PhaseLocked Systems
 


Judd Storrs wrote:

> I'm pretty sure the constructor is called if Matlab doesn't have a
> class definition. Suppose:
>
> @FooBar/FooBar.m:
> function obj = FooBar()
>      disp('Hello!');
>      obj.data = 'Baz';
>      obj = class(obj, 'FooBar');
>
> Then at least in Matlab R14 (7.0.1) (I've edited whitespace):
>
> Matlab Session 1:
> >> obj1 = FooBar()
> Hello!
> obj1 = FooBar object: 1-by-1
> >> save('test.mat','obj1')
> >> obj2 = FooBar()        
> Hello!
> obj2 = FooBar object: 1-by-1
> >> save('test2.mat','obj2')
> >> exit
>
> Matlab Session 2:
> >> load test.mat
> Hello!
> >> load test2.mat
> >>
>
> As you see in the second session, matlab does execute the constructor
> the first time it encounters the FooBar class, but once it knews the
> class structure, it didn't need to call the constructor again.
>
> --judd

I was playing around in Matlab R2007b.

Create

@Foo/Foo.m

function f = Foo()


d2/@Foo/Foo.m:





Warning: Could not determine the fields for class Foo by calling the
         constructor with no input arguments.  The object a has been
         converted to a structure.  To eliminate the conversion create
         an object of class Foo before calling LOAD.


On Thu, Apr 23, 2009 at 10:09 AM, Robert T. Short <octave@...> wrote:
John W. Eaton wrote:
On 22-Apr-2009, Robert T. Short wrote:

| John W. Eaton wrote:
| > On 22-Apr-2009, Robert T. Short wrote:
| >
| > | I hadn't thought of that.  Good question and the answer is that there is | > | no guarantee.  In fact, some of my "real" classes require arguments. | > | | > | I think I have to go back to the drawing board on this. | >
| > I suggest that you try saving and reloading one of those classes
| > (preferably one with a parent class) in Matlab and see what happens.
| > Does it work?
| >
| > jwe
| >
| >
| >   | Yes it works, but it occurs to me that I hadn't checked to see if all | the DETAILS worked right.

Did you try it to see what happens when a constructor requires an
argument?

jwe


 
I tried a bunch of stuff last night including the argument case.  MATLAB gets it right every time.  I walked through a .mat file with both the aggregation and inheritance case and there is nothing in the file that tells which it is, so somehow MATLAB figures it out from the sources or something.  Somebody over at the Mathworks is smarter than I am.

I doubt I will really be able to spend enough time before the weekend to make a real impact, but I will eyeball your diff and the stuff you sent out yesterday.

I think I have a workable short term solution.  I will type that up as soon as I can.  I can get that implemented and at least we will have a complete solution, if a little imperfect.

Bob


Taking the example further change @Foo/Foo.m to

function f = Foo(f)
  f = class(f, 'Foo');

Then in matlab:

>> load test.mat                 
Warning: Could not determine the fields for class Foo by calling the
         constructor with no input arguments.  The object a has been
         converted to a structure.  To eliminate the conversion create
         an object of class Foo before calling LOAD.
>> f.dataelement = 'I am Foo';
>> f = Foo(f);
>> load test.mat                 
>> bar    

bar =

    Foo object: 1-by-1

Ok, restart matlab

>> f.notdataelement = 'I am not Foo';
>> f = Foo(f);                      
>> load test.mat                    
Warning: Fields of object 'bar' do not match the current constructor
         definition for class 'Foo'.  The object has been converted
         to a structure.


--judd




On Thu, Apr 23, 2009 at 11:46 AM, John W. Eaton <jwe@...> wrote:
On 23-Apr-2009, Judd Storrs wrote:

| So, I think Matlab keeps an internal memory of each class structure.
| Probably the first time a class of a specific type is created, Matlab
| remembers the member information. If Matlab doesn't have member information
| it calls the constructor with no arguments. I'll do some more testing to
| verify.

OK.  Is that information separate from any objects that are in the
symbol table?  I.e., does "clear all" followed by loading the file
result in the error message when there is no valid constructor?

Clear all doesn't seem to clear the class memory

>> bar = Foo()
bar = Foo object: 1-by-1
>> save test.mat bar
>> clear all
>> load test.mat
>> class(bar)
ans = Foo

Outside of matlab, break @Foo/Foo.m

>> clear all
>> load test.mat
>> class(bar)
ans =Foo
>> clear all
>> bar2 = Foo()
bar2 = []
>> load test.mat
>> class(bar)
ans = Foo


What happens if you modify the constructor (for example, to add or
remove a field from the class structure) when Matlab is running and
already has some object(s) created using the previous constructor?
Does that generate an error?

Reset @Foo/Foo.m
function Foo ()
  f.dataelement = 'I am Foo';
  f = class(f,'Foo');

>> bar = Foo()            
bar = Foo object: 1-by-1
>> struct(bar)
ans = dataelement: 'I am Foo'

Outside matlab add a new field to @Foo/Foo.m:
function Foo ()
  f.dataelement = 'I am Foo';
  f.newelement = 'I am even more Foo';
  f = class(f,'Foo');

>> bar2 = Foo()
??? Error using ==> class
Number of fields for class Foo cannot be changed without clear classes.

Error in ==> Foo.Foo at 4
  f = class(f,'Foo');

>> clear classes
>> bar2 = Foo()
bar2 = Foo object: 1-by-1
>> class(bar)
??? Error using ==> bar at 48
Not enough input arguments.
>> struct(bar2)
ans =
    dataelement: 'I am Foo'
     newelement: 'I am even more Foo'
>> struct(bar)
??? Error using ==> bar at 48
Not enough input arguments.
>> help clear
...
    CLEAR CLASSES is the same as CLEAR ALL except that class definitions
    are also cleared. If any objects exist outside the workspace (say in
    userdata or persistent in a locked m-file) a warning will be issued and
    the class definition will not be cleared. CLEAR CLASSES must be used if
    the number or names of fields in a class are changed.
...

--judd



Re: OOP load update

by John W. Eaton-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On  6-May-2009, Robert T. Short wrote:

| if there might be an inheritance issue then
|   if there is no entry in the exemplar table then
|      execute the class constructor to create an exemplar table entry
|      if executing the class constructor failed, return the broken class
|
|   use the exemplar table to reconstruct the inheritance
|   if there is an inconsistency in the inheritance structure return the
| broken class
|
| if there is no inheritance problem
|   execute the user's loadobj method

Don't you need to add

    if there is no entry in the exemplar table then
      execute the class constructor to create an exemplar table entry
      if executing the class constructor failed, return the broken class

here as well, so that you can check for consistency?  So does it
matter whether there is inheritance or not?  Just first ensure that
there is an exemplar for the named class in the table.  Then you can
check to see about inheritance and consistency of field names.

|   check the class for consistency with the exemplar table entry
|   if inconsistent, return the broken class
|   else return the class

jwe

Re: OOP load update

by John W. Eaton-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On  6-May-2009, Robert T. Short wrote:

| The only difference between the above and what is done now is that I
| don't currently execute the class constructor.

The change that I suggested earlier called the constructor if the
exemplar table did not have an object with the given name.  I thought
you used that as a starting point for your most recent changes.  My
patch was posted here, in case you missed it:

  https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2009-April/012050.html

The patch I'm talking about is the attachment, not the one that I
checked in to savannah.

jwe

Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Correct.  Obviously, I will think this through carefully before I do the
code.  I am thinking that this isn't hard, but the logic does seem a
little convoluted.  I haven't spent the time yet to see what
simplifications I can muster.

Bob
--
Robert T. Short
PhaseLocked Systems


John W. Eaton wrote:

> On  6-May-2009, Robert T. Short wrote:
>
> | if there might be an inheritance issue then
> |   if there is no entry in the exemplar table then
> |      execute the class constructor to create an exemplar table entry
> |      if executing the class constructor failed, return the broken class
> |
> |   use the exemplar table to reconstruct the inheritance
> |   if there is an inconsistency in the inheritance structure return the
> | broken class
> |
> | if there is no inheritance problem
> |   execute the user's loadobj method
>
> Don't you need to add
>
>     if there is no entry in the exemplar table then
>       execute the class constructor to create an exemplar table entry
>       if executing the class constructor failed, return the broken class
>
> here as well, so that you can check for consistency?  So does it
> matter whether there is inheritance or not?  Just first ensure that
> there is an exemplar for the named class in the table.  Then you can
> check to see about inheritance and consistency of field names.
>
> |   check the class for consistency with the exemplar table entry
> |   if inconsistent, return the broken class
> |   else return the class
>
> jwe
>
>
>  


Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I don't remember seeing this patch.  Thanks.  I will eyeball it.

John W. Eaton wrote:

> On  6-May-2009, Robert T. Short wrote:
>
> | The only difference between the above and what is done now is that I
> | don't currently execute the class constructor.
>
> The change that I suggested earlier called the constructor if the
> exemplar table did not have an object with the given name.  I thought
> you used that as a starting point for your most recent changes.  My
> patch was posted here, in case you missed it:
>
>   https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2009-April/012050.html
>
> The patch I'm talking about is the attachment, not the one that I
> checked in to savannah.
>
> jwe
>
>
>  


Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I lied.  I do remember this patch, and used it until you posted the
"real" patch.

John W. Eaton wrote:

> On  6-May-2009, Robert T. Short wrote:
>
> | The only difference between the above and what is done now is that I
> | don't currently execute the class constructor.
>
> The change that I suggested earlier called the constructor if the
> exemplar table did not have an object with the given name.  I thought
> you used that as a starting point for your most recent changes.  My
> patch was posted here, in case you missed it:
>
>   https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2009-April/012050.html
>
> The patch I'm talking about is the attachment, not the one that I
> checked in to savannah.
>
> jwe
>
>
>  


Re: OOP load update

by John W. Eaton-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On  6-May-2009, Robert T. Short wrote:

| I lied.  I do remember this patch, and used it until you posted the
| "real" patch.

What do you mean by "real" patch?

The one I checked in:

  http://hg.savannah.gnu.org/hgweb/octave/rev/d8f9588c6ba1

is not the same as the one that appears in the attachment of the
message:

  https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2009-April/012050.html

an earlier version of which appears here:

  https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2009-April/012019.html

The patches that I posted as attachments are for loading objects from
files.  That's why I thought you used them as the basis for your
recent patch.

The patch that I checked in just added the exemplar infrastructure,
and used it in the class constructor to ensure that all objects of a
given class have the same layout as the first one that is constructed.

jwe

Re: OOP load update

by Robert T. Short :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I am probably confused now.  The "real" patch is the one you checked in
through Savannah.  I used the other one a little bit, but that was the
period I was traveling and had no time to do this so I never got very
far with it.  Thus when I saw you had checked in the exemplar stuff I
grabbed it and ran with it.  Don't think I ever really got back to
looking at the other patch again so I probably didn't really use it as a
basis for anything.  I will take another look at it.


John W. Eaton wrote:

> On  6-May-2009, Robert T. Short wrote:
>
> | I lied.  I do remember this patch, and used it until you posted the
> | "real" patch.
>
> What do you mean by "real" patch?
>
> The one I checked in:
>
>   http://hg.savannah.gnu.org/hgweb/octave/rev/d8f9588c6ba1
>
> is not the same as the one that appears in the attachment of the
> message:
>
>   https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2009-April/012050.html
>
> an earlier version of which appears here:
>
>   https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2009-April/012019.html
>
> The patches that I posted as attachments are for loading objects from
> files.  That's why I thought you used them as the basis for your
> recent patch.
>
> The patch that I checked in just added the exemplar infrastructure,
> and used it in the class constructor to ensure that all objects of a
> given class have the same layout as the first one that is constructed.
>
> jwe
>
>
>