Old-style class constructor must assign fields in the SAME ORDER every time...?

I’m struggling through the very poorly documented OOP features of Octave, and I just wanted to share a discovery which cost me hours of time in the hopes of saving others some hours of their time. :slight_smile:

Every branch of the constructor must create the fields of the structure in the same order, or else you’ll get a failure with the cryptic error message error: mismatch in field names.

For example, the following very reasonable-looking constructor contains a bug:

function obj = test (a, b, c)

  if nargin == 0
    obj.a = obj.b = obj.c = 0;
    obj = class(obj, "test");
  elseif nargin == 3
    obj.a = a;
    obj.b = b;
    obj.c = c;
    obj = class(obj, "test");
  endif

endfunction

The problem is that the nargin == 0 branch assigns the fields in the order c then b then a, whereas the nargin == 3 branch assigns them in the opposite order: a first, then b, then c.

>> a = test();
>> b = test(1,2,3);
error: mismatch in field names
error: called from
    test at line 10 column 9
>>

I haven’t seen a warning about this in the documentation, but it’s my opinion that there should be!

Solomon

You might want to use classdef for object oriented programming rather than the old @class syntax. There is very little OOP code being written in this way anymore. Also, since there is already a function test.m it would be better to use a name that doesn’t overlap with an existing Octave function. I changed the code to use the name tstclass and confirm that the obscure error message “mismatch in field names” is emitted.

1 Like

I was under the impression that classdef was not usable yet? For example, GNU Octave - Bugs: bug #44665, error in concatenation of classdef... [Savannah] seems to suggest that I can’t even make arrays of classdef objects? That would be a deal-breaker for me, which is why I decided to ignore classdef and plunge into old-style objects.

Thanks, this is very appreciated :+1:

I would rephrase this sentence to: “classdef is not Matlab compatible yet” or “classdef is not feature-complete yet”.

Basic classdef features are working well for my projects and the syntax is much “cleaner” than the old style classes. You wouldn’t stumble over the error you encountered. Old-style classes are structs with a little painting on top (a typecast at the end of the constructor and methods registered for them). All rules for structs given in the documentation about structs in the Octave manual (including the order of initialization) apply, which is the documentation to consult first.

However, when using classdef, “bleeding edge” features (introduced into Matlab within the last 10 years [classdef was introduced about 2008]) can be buggy or behave differently from Matlab, like the array concatenation. Try out each feature you want to use and you will not encounter many surprises if you keep you classdefs simple :slightly_smiling_face:

1 Like

Thank you! I’m going to go ahead and use classdef classes. And I’m in agreement, they are much cleaner!

Do I still need to implement subsasgn and subsref if I want to be able to refer to public properties using the . operator? Or does being public mean that they just work without needing those methods to be written explicitly?

Edit: empirically, I’m finding that I do not need to implement subsasgn (obj.prop=3 just does the right thing) but I do need to implement subsref (or else obj.prop on its own will throw an error.) Something smells wrong about this, but I can live with it! Here’s what I’m writing for subsref:

    function r = subsref(obj, idx)
      if (strcmp (idx.type, ".") && any (strcmp (idx.subs, {"prop1", "prop2", "prop3"})))
        r = obj.(idx.subs);
      endif
    endfunction

That’s the sort of thing I was expecting would be unnecessary with classdef classes. No?

Yes, this shouldn’t be necessary. The dot notation from structures works with the properties of a classdef object. Here’s simple code that shows it working for me. I put this in the file tstclass.m

classdef tstclass

  properties
    prop1
    prop2
    prop3
  endproperties

endclassdef

Octave session using this class:

octave:5> obj = tstclass
obj =

  tstclass m_object with properties:

      prop1: [0x0 double]
      prop2: [0x0 double]
      prop3: [0x0 double]

octave:6> obj.prop1 = pi
obj =

  tstclass m_object with properties:

      prop1: [1x1 double]
      prop2: [0x0 double]
      prop3: [0x0 double]

octave:7> obj.prop1
ans = 3.1416
1 Like

I have been using classdef for a project that also needs to work with matlab and I have no need to define a subsref method like that. It just works. :slight_smile:

Notice that also in order to be in a safe side you should take into account that you need to deal with all the levels in idx. As an example assume that in your example r is a struct with an l field, then a call like obj.r.l would not work.

The usual pattern, at least for me, is to deal with one level at the time and then at the end call obj = builtin('subsref',obj,idxs);. As a convention I always change the return to be obj and I remove the elements from idxs when I use them.

Thanks, rik. Indeed I’ve found this to be true most of the time. I’ll have to sit down with my one class where that seems to be failing and isolate the problem; I’m sure it was a simple mistake on my part, but if I can’t figure it out you know I’ll be back here asking again! :slight_smile:

Hmm… I understand what you’re saying in principle but it’s going to take me some fumbling to figure out how to actually do this. :slight_smile: