Search code examples
classooppropertiesoctavegetter-setter

How to access/read class instance attributes in octave?


Making a class similar to that one in the example I attached arguments to the class instance like so

function t = train (m, F_z, F_b, varargin)
  ...
  t.m =     m;      % total mass of train [kg]
  t.F_z =  F_z;     % ...
  ...
  t = class (t, "train");

Getting the fieldnames works

>> t1 = train(100, 150000, 200000);
...
>> fieldnames(t1)
ans =
{
  [1,1] = m
  [2,1] = F_z
  ...

But how do I access these? Apparently it's not

>> t1.m
error: invalid index for class
>> getfield(t1, 'm')
error: invalid index for class
error: called from
...

If I leave out the line t = class (t, 'train'); at the end of function t = train (m... in @train/train.m these things all seem to work fine... But then its a struct and not a class


Solution

  • You can do this in two ways. You can define a method to retrieve it (easy), or you set up subsref (subscript reference) (hard).

    To define a method, simply create @train/m.m with:

    function r = m ()
      r = t.m;
    endfunction
    

    To set up subscripting, then create @train/subsref.m with:

    function r = subsref (val, idx)
      if (strcmp (idx.type, ".") && strcmp (idx.subs, "m"))
        r = val.m;
      endif
    endfunction
    

    This will allow you to access only the field m. To give access to any of the properties within your class, you can simply do:

    function r = subsref (val, idx)
      r = subsref (struct (val), idx);
    endfunction
    

    To allow access to a subset of properties, you can:

    function r = subsref (val, idx)
      if (strcmp (idx.type, ".") && any (strcmp (idx.subs, {"m", "f2", "F_z"})))
        r = val.(idx.subs{1});
      endif
    endfunction
    

    But subsref controls all indexing, not only when accessing it as a structure. You could allow people to index the object (maybe you have a wagon class, and indexing the train returns them). The function looks a bit weird at start (just like subsasgn (subscript assignment)) but can be quite useful. You could do:

    function r = subsref (val, idx)
      if (strcmp (idx.type, ".") && strcmp (idx.subs, "m"))
        r = val.m;
      elseif (strcmp (idx.type, "()"))
        r = val.wagons(idx.subs{:});
      endif
    endfunction