Object Oriented Programming
***************************

Octave has the ability to create user-defined classes---including the capabilities of operator and function overloading.  Classes can protect internal properties so that they may not be altered accidentally which facilitates data encapsulation.  In addition, rules can be created to address the issue of class precedence in mixed class operations.

This chapter discusses the means of constructing a user class, how to query and set the properties of a class, and how to overload operators and functions. Throughout this chapter real code examples are given using a class designed for polynomials.

Creating a Class
================

This chapter illustrates user-defined classes and object oriented programming through a custom class designed for polynomials.  This class was chosen for its simplicity which does not distract unnecessarily from the discussion of the programming features of Octave.  Even so, a bit of background on the goals of the polynomial class is necessary before the syntax and techniques of Octave object oriented programming are introduced.

The polynomial class is used to represent polynomials of the form

     a0 + a1 * x + a2 * x^2 + ... + an * x^n

where a0, a1, etc. are real scalars. Thus the polynomial can be represented by a vector

     a = [a0, a1, a2, ..., an];

This is a sufficient specification to begin writing the constructor for the polynomial class.  All object oriented classes in Octave must be located in a directory that is the name of the class prepended with the @ symbol. For example, the polynomial class will have all of its methods defined in the @polynomial directory.

The constructor for the class must be the name of the class itself; in this example the constructor resides in the file @polynomial/polynomial.m. Ideally, even when the constructor is called with no arguments it should return a valid object.  A constructor for the polynomial class might look like

     ## -*- texinfo -*-
     ## @deftypefn  {} {} polynomial ()
     ## @deftypefnx {} {} polynomial (@var{a})
     ## Create a polynomial object representing the polynomial
     ##
     ## @example
     ## a0 + a1 * x + a2 * x^2 + @dots{} + an * x^n
     ## @end example
     ##
     ## @noindent
     ## from a vector of coefficients [a0 a1 a2 @dots{} an].
     ## @end deftypefn

     function p = polynomial (a)

       if (nargin == 0)
         p.poly = 0;
         p = class (p, "polynomial");
       else
         if (isa (a, "polynomial"))
           p = a;
         elseif (isreal (a) && isvector (a))
           p.poly = a(:).';  # force row vector
           p = class (p, "polynomial");
         else
           error ("@polynomial: A must be a real vector");
         endif
       endif

     endfunction

Note that the return value of the constructor must be the output of the class function.  The first argument to the class function is a structure and the second is the name of the class itself.  An example of calling the class constructor to create an instance is

     p = polynomial ([1, 0, 1]);

Methods are defined by m-files in the class directory and can have embedded documentation the same as any other m-file.  The help for the constructor can be obtained by using the constructor name alone, that is, for the polynomial constructor help polynomial will return the help string.  Help can be restricted to a particular class by using the class directory name followed by the method.  For example, help @polynomial/polynomial is another way of displaying the help string for the polynomial constructor.  This second means is the only way to obtain help for the overloaded methods and functions of a class.

The same specification mechanism can be used wherever Octave expects a function name.  For example type @polynomial/disp will print the code of the disp method of the polynomial class to the screen, and dbstop @polynomial/disp will set a breakpoint at the first executable line of the disp method of the polynomial class.

To check whether a variable belongs to a user class, the isobject and isa functions can be used.  For example:

     p = polynomial ([1, 0, 1]);
     isobject (p)
       => 1
     isa (p, "polynomial")
       => 1

 -- TF =: isobject (X)
     Return true if X is a class object. class, typeinfo, isa, ismethod, isprop

The available methods of a class can be displayed with the methods function.

 -- methods: (OBJ)
 -- methods: ("CLASSNAME")
 -- methods: (..., "-full")
 -- MTDS =: methods (...)
     List the names of the public methods for the object OBJ or the named class CLASSNAME.

     OBJ may be an Octave class object or a Java object. CLASSNAME may be the name of an Octave class or a Java class.

     If the optional argument "-full" is given then Octave returns full method signatures which include output type, name of method, and the number and type of inputs.

     When called with no output arguments, methods prints the list of method names to the screen.  Otherwise, the output argument MTDS contains the list in a cell array of strings. ismethod, properties, fieldnames

To inquire whether a particular method exists for a user class, the ismethod function can be used.

 -- TF =: ismethod (OBJ, METHOD)
 -- TF =: ismethod (CLASS_NAME, METHOD)
     Return true if the string METHOD is a valid method of the object OBJ or of the class CLSNAME. isprop, isobject, isjava, methods

For a polynomial class it makes sense to have a method to compute its roots.

     function r = roots (p)
       r = roots (fliplr (p.poly));
     endfunction

We can check for the existence of the roots-method by calling:

     p = polynomial ([1, 0, 1]);
     ismethod (p, "roots")
       => 1

Class Methods
=============

There are a number of basic class methods that can (and should) be defined to allow the contents of the classes to be queried and set.  The most basic of these is the disp method.  The disp method is used by Octave whenever a class should be displayed on the screen.  Usually this is the result of an Octave expression that doesn't end with a semicolon.  If this method is not defined, then Octave won't print anything when displaying the contents of a class which can be confusing.

An example of a disp method for the polynomial class might be

     function disp (p)

       a = p.poly;
       first = true;
       for i = 1 : length (a);
         if (a(i) != 0)
           if (first)
             first = false;
           elseif (a(i) > 0 || isnan (a(i)))
             printf (" +");
           endif
           if (a(i) < 0)
             printf (" -");
           endif
           if (i == 1)
             printf (" %.5g", abs (a(i)));
           elseif (abs (a(i)) != 1)
             printf (" %.5g *", abs (a(i)));
           endif
           if (i > 1)
             printf (" X");
           endif
           if (i > 2)
             printf (" ^ %d", i - 1);
           endif
         endif
       endfor

       if (first)
         printf (" 0");
       endif
       printf ("\n");

     endfunction

To be consistent with the Octave graphic handle classes, a class should also define the get and set methods.  The get method accepts one or two arguments.  The first argument is an object of the appropriate class.  If no second argument is given then the method should return a structure with all the properties of the class.  If the optional second argument is given it should be a property name and the specified property should be retrieved.

     function val = get (p, prop)

       if (nargin < 1)
         print_usage ();
       endif

       if (nargin == 1)
         val.poly = p.poly;
       else
         if (! ischar (prop))
           error ("@polynomial/get: PROPERTY must be a string");
         endif

         switch (prop)
           case "poly"
             val = p.poly;
           otherwise
             error ('@polynomial/get: invalid PROPERTY "%s"', prop);
         endswitch
       endif

     endfunction

Similarly, the first argument to the set method should be an object and any additional arguments should be property/value pairs.

     function pout = set (p, varargin)

       if (numel (varargin) < 2 || rem (numel (varargin), 2) != 0)
         error ("@polynomial/set: expecting PROPERTY/VALUE pairs");
       endif

       pout = p;
       while (numel (varargin) > 1)
         prop = varargin{1};
         val  = varargin{2};
         varargin(1:2) = [];
         if (! ischar (prop) || ! strcmp (prop, "poly"))
           error ("@polynomial/set: invalid PROPERTY for polynomial class");
         elseif (! (isreal (val) && isvector (val)))
           error ("@polynomial/set: VALUE must be a real vector");
         endif

         pout.poly = val(:).';  # force row vector
       endwhile

     endfunction

Note that Octave does not implement pass by reference; Therefore, to modify an object requires an assignment statement using the return value from the set method.

     p = set (p, "poly", [1, 0, 0, 0, 1]);

The set method makes use of the subsasgn method of the class, and therefore this method must also be defined.  The subsasgn method is discussed more thoroughly in the next section (see Indexing Objects).

Finally, user classes can be considered to be a special type of a structure, and they can be saved to a file in the same manner as a structure.  For example:

     p = polynomial ([1, 0, 1]);
     save userclass.mat p
     clear p
     load userclass.mat

All of the file formats supported by save and load are supported. In certain circumstances a user class might contain a field that it doesn't make sense to save, or a field that needs to be initialized before it is saved. This can be done with the saveobj method of the class.

 -- B =: saveobj (A)
     Method of a class to manipulate an object prior to saving it to a file.

     The function saveobj is called when the object A is saved using the save function.  An example of the use of saveobj might be to remove fields of the object that don't make sense to be saved or it might be used to ensure that certain fields of the object are initialized before the object is saved.  For example:

          function b = saveobj (a)
            b = a;
            if (isempty (b.field))
               b.field = initfield (b);
            endif
          endfunction

     loadobj, class

saveobj is called just prior to saving the class to a file.  Similarly, the loadobj method is called just after a class is loaded from a file, and can be used to ensure that any removed fields are reinserted into the user object.

 -- B =: loadobj (A)
     Method of a class to manipulate an object after loading it from a file.

     The function loadobj is called when the object A is loaded using the load function.  An example of the use of saveobj might be to add fields to an object that don't make sense to be saved. For example:

          function b = loadobj (a)
            b = a;
            b.addmissingfield = addfield (b);
          endfunction

     saveobj, class

Indexing Objects
================

Defining Indexing And Indexed Assignment
----------------------------------------

Objects can be indexed with parentheses or braces, either like OBJ(IDX) or like OBJ{IDX}, or even like OBJ(IDX).FIELD.  However, it is up to the programmer to decide what this indexing actually means.  In the case of the polynomial class P(N) might mean either the coefficient of the N-th power of the polynomial, or it might be the evaluation of the polynomial at N.  The meaning of this subscripted referencing is determined by the subsref method.

 -- NEWVAL =: subsref (VAL, IDX)
     Perform the subscripted element selection operation on VAL according to the subscript specified by IDX.

     The subscript IDX must be a structure array with fields type and subs.  Valid values for type are "()", "{}", and ".".  The subs field may be either ":" or a cell array of index values.

     The following example shows how to extract the first two columns of a matrix

          val = magic (3)
              => val = [ 8   1   6
                         3   5   7
                         4   9   2 ]
          idx.type = "()";
          idx.subs = {":", 1:2};
          subsref (val, idx)
               => [ 8   1
                    3   5
                    4   9 ]

     Note that this is the same as writing val(:, 1:2).

     If IDX is an empty structure array with fields type and subs, return VAL.

     The keyword end cannot be used within subsref for indexing assignments. subsasgn, substruct

For example, this class uses the convention that indexing with "()" evaluates the polynomial and indexing with "{}" returns the N-th coefficient (of the N-th power).  The code for the subsref method looks like

     function r = subsref (p, s)

       if (isempty (s))
         error ("@polynomial/subsref: missing index");
       endif

       switch (s(1).type)

         case "()"
           idx = s(1).subs;
           if (numel (idx) != 1)
             error ("@polynomial/subsref: need exactly one index");
           endif
           r = polyval (fliplr (p.poly), idx{1});

         case "{}"
           idx = s(1).subs;
           if (numel (idx) != 1)
             error ("@polynomial/subsref: need exactly one index");
           endif

           if (isnumeric (idx{1}))
             r = p.poly(idx{1}+1);
           else
             r = p.poly(idx{1});
           endif

         case "."
           fld = s.subs;
           if (! strcmp (fld, "poly"))
             error ('@polynomial/subsref: invalid property "%s"', fld);
           endif
           r = p.poly;

         otherwise
           error ("@polynomial/subsref: invalid subscript type");

       endswitch

       if (numel (s) > 1)
         r = subsref (r, s(2:end));
       endif

     endfunction

The equivalent functionality for subscripted assignments uses the subsasgn method.

 -- NEWVAL =: subsasgn (VAL, IDX, RHS)
     Perform the subscripted assignment operation according to the subscript specified by IDX.

     The subscript IDX must be a structure array with fields type and subs.  Valid values for type are "()", "{}", and ".".  The subs field may be either ":" or a cell array of index values.

     The following example shows how to set the two first columns of a 3-by-3 matrix to zero.

          val = magic (3);
          idx.type = "()";
          idx.subs = {":", 1:2};
          val = subsasgn (val, idx, 0)
               =>  [ 0   0   6
                     0   0   7
                     0   0   2 ]

     Note that this is the same as writing val(:, 1:2) = 0.

     If IDX is an empty structure array with fields type and subs, return RHS.

     The keyword end cannot be used within subsasgn for indexing assignments. subsref, substruct, optimize_subsasgn_calls

 -- VAL =: optimize_subsasgn_calls ()
 -- OLD_VAL =: optimize_subsasgn_calls (NEW_VAL)
 -- OLD_VAL =: optimize_subsasgn_calls (NEW_VAL, "local")
     Query or set the internal flag for subsasgn method call optimizations.

     If true, Octave will attempt to eliminate the redundant copying when calling the subsasgn method of a user-defined class.

     When called from inside a function with the "local" option, the variable is changed locally for the function and any subroutines it calls. The original variable value is restored when exiting the function. subsasgn

Note that the subsref and subsasgn methods always receive the whole index chain, while they usually handle only the first element.  It is the responsibility of these methods to handle the rest of the chain (if needed), usually by forwarding it again to subsref or subsasgn.

If you wish to use the end keyword in subscripted expressions of an object, then there must be an end method defined.  For example, the end method for the polynomial class might look like

     function r = end (obj, index_pos, num_indices)

       if (num_indices != 1)
         error ("polynomial object may only have one index");
       endif

       r = length (obj.poly) - 1;

     endfunction

which is a fairly generic end method that has a behavior similar to the end keyword for Octave Array classes.  An example using the polynomial class is then

     p = polynomial ([1,2,3,4]);
     p{end-1}
       => 3

Objects can also be used themselves as the index in a subscripted expression and this is controlled by the subsindex function.

 -- IDX =: subsindex (OBJ)
     Convert an object to an index vector.

     When OBJ is a class object defined with a class constructor, then subsindex is the overloading method that allows the conversion of this class object to a valid indexing vector.  It is important to note that subsindex must return a zero-based real integer vector of the class "double".  For example, if the class constructor were

          function obj = myclass (a)
            obj = class (struct ("a", a), "myclass");
          endfunction

     then the subsindex function

          function idx = subsindex (obj)
            idx = double (obj.a) - 1.0;
          endfunction

     could be used as follows

          a = myclass (1:4);
          b = 1:10;
          b(a)
          => 1  2  3  4

     class, subsref, subsasgn

Finally, objects can be used like ranges by providing a colon method.

 -- R =: colon (BASE, LIMIT)
 -- R =: colon (BASE, INCREMENT, LIMIT)
     Return the result of the colon expression corresponding to BASE, LIMIT, and optionally, INCREMENT.

     This function is equivalent to the operator syntax BASE : LIMIT or BASE : INCREMENT : LIMIT. linspace

Indexed Assignment Optimization
-------------------------------

Octave's ubiquitous lazily-copied pass-by-value semantics implies a problem for performance of user-defined subsasgn methods.  Imagine the following call to subsasgn

     ss = substruct ("()", {1});
     x = subsasgn (x, ss, 1);

where the corresponding method looking like this:

     function x = subsasgn (x, ss, val)
       ...
       x.myfield (ss.subs{1}) = val;
     endfunction

The problem is that on entry to the subsasgn method, x is still referenced from the caller's scope, which means that the method will first need to unshare (copy) x and x.myfield before performing the assignment.  Upon completing the call, unless an error occurs, the result is immediately assigned to x in the caller's scope, so that the previous value of x.myfield is forgotten.  Hence, the Octave language implies a copy of N elements (N being the size of x.myfield), where modifying just a single element would actually suffice.  In other words, a constant-time operation is degraded to linear-time one.  This may be a real problem for user classes that intrinsically store large arrays.

To partially solve the problem Octave uses a special optimization for user-defined subsasgn methods coded as m-files.  When the method gets called as a result of the built-in assignment syntax (not a direct subsasgn call as shown above), i.e., x(1) = 1,  AND if the subsasgn method is declared with identical input and output arguments, as in the example above, then Octave will ignore the copy of x inside the caller's scope; therefore, any changes made to x during the method execution will directly affect the caller's copy as well. This allows, for instance, defining a polynomial class where modifying a single element takes constant time.

It is important to understand the implications that this optimization brings. Since no extra copy of x will exist in the caller's scope, it is solely the callee's responsibility to not leave x in an invalid state if an error occurs during the execution.  Also, if the method partially changes x and then errors out, the changes will affect x in the caller's scope.  Deleting or completely replacing x inside subsasgn will not do anything, however, only indexed assignments matter.

Since this optimization may change the way code works (especially if badly written), a function optimize_subsasgn_calls is provided to control it.  This feature is enabled by default.  Another way to avoid the optimization is to declare subsasgn methods with different output and input arguments like this:

     function y = subsasgn (x, ss, val)
       ...
     endfunction

Overloading Objects
===================

Function Overloading
--------------------

Any Octave function can be overloaded, and this allows an object-specific version of a function to be called as needed.  A pertinent example for the polynomial class might be to overload the polyval function.

     function [y, dy] = polyval (p, varargin)

       if (nargout > 1)
         [y, dy] = polyval (fliplr (p.poly), varargin{:});
       else
         y = polyval (fliplr (p.poly), varargin{:});
       endif

     endfunction

This function just hands off the work to the normal Octave polyval function.  Another interesting example of an overloaded function for the polynomial class is the plot function.

     function h = plot (p, varargin)

       n = 128;
       rmax = max (abs (roots (p.poly)));
       x = [0 : (n - 1)] / (n - 1) * 2.2 * rmax - 1.1 * rmax;
       if (nargout > 0)
         h = plot (x, polyval (p, x), varargin{:});
       else
         plot (x, polyval (p, x), varargin{:});
       endif

     endfunction

which allows polynomials to be plotted in the domain near the region of the roots of the polynomial.

Functions that are of particular interest for overloading are the class conversion functions such as double.  Overloading these functions allows the cast function to work with a user class.  It can also aid in the use of a class object with methods and functions from other classes since the object can be transformed to the requisite input form for the new function. An example double function for the polynomial class might look like

     function a = double (p)
       a = p.poly;
     endfunction

Operator Overloading
--------------------

The following table shows, for each built-in numerical operation, the corresponding function name to use when providing an overloaded method for a user class.

Operation   Method   Description
a + b   plus (a, b)   Binary addition
a - b   minus (a, b)   Binary subtraction
+a   uplus (a)   Unary addition
-a   uminus (a)   Unary subtraction
a .* b   times (a, b)   Element-wise multiplication
a * b   mtimes (a, b)   Matrix multiplication
a ./ b   rdivide (a, b)   Element-wise right division
a / b   mrdivide (a, b)   Matrix right division
a .\ b   ldivide (a, b)   Element-wise left division
a \ b   mldivide (a, b)   Matrix left division
a .^ b   power (a, b)   Element-wise power
a ^ b   mpower (a, b)   Matrix power
a < b   lt (a, b)   Less than
a <= b   le (a, b)   Less than or equal to
a > b   gt (a, b)   Greater than
a >= b   ge (a, b)   Greater than or equal to
a == b   eq (a, b)   Equal to
a != b   ne (a, b)   Not equal to
a & b   and (a, b)   Logical and
a | b   or (a, b)   Logical or
!a   not (a)   Logical not
a'   ctranspose (a)   Complex conjugate transpose
a.'   transpose (a)   Transpose
a:b   colon (a, b)   Two element range
a:b:c   colon (a, b, c)   Three element range
[a, b]   horzcat (a, b)   Horizontal concatenation
[a; b]   vertcat (a, b)   Vertical concatenation
a(s_1,...,s_n)   subsref (a, s)   Subscripted reference
a(s_1,...,s_n) = b   subsasgn (a, s, b)   Subscripted assignment
b(a)   subsindex (a)   Convert object to index
disp   disp (a)   Object display

Table: Available overloaded operators and their corresponding class method

An example mtimes method for the polynomial class might look like

     function p = mtimes (a, b)
       p = polynomial (conv (double (a), double (b)));
     endfunction

Precedence of Objects
---------------------

Many functions and operators take two or more arguments and the situation can easily arise where these functions are called with objects of different classes.  It is therefore necessary to determine the precedence of which method from which class to call when there are mixed objects given to a function or operator.  To do this the superiorto and inferiorto functions can be used

 -- superiorto: (CLASS_NAME, ...)
     When called from a class constructor, mark the object currently constructed as having a higher precedence than CLASS_NAME.

     More that one such class can be specified in a single call.  This function may only be called from a class constructor. inferiorto

 -- inferiorto: (CLASS_NAME, ...)
     When called from a class constructor, mark the object currently constructed as having a lower precedence than CLASS_NAME.

     More that one such class can be specified in a single call.  This function may only be called from a class constructor. superiorto

With the polynomial class, consider the case

     2 * polynomial ([1, 0, 1]);

that mixes an object of the class "double" with an object of the class "polynomial".  In this case the return type should be "polynomial" and so the superiorto function is used in the class constructor.  In particular the polynomial class constructor would be modified to

     ## -*- texinfo -*-
     ## @deftypefn  {} {} polynomial ()
     ## @deftypefnx {} {} polynomial (@var{a})
     ## Create a polynomial object representing the polynomial
     ##
     ## @example
     ## a0 + a1 * x + a2 * x^2 + @dots{} + an * x^n
     ## @end example
     ##
     ## @noindent
     ## from a vector of coefficients [a0 a1 a2 @dots{} an].
     ## @end deftypefn

     function p = polynomial (a)

       if (nargin == 0)
         p.poly = [0];
         p = class (p, "polynomial");
       else
         if (strcmp (class (a), "polynomial"))
           p = a;
         elseif (isreal (a) && isvector (a))
           p.poly = a(:).';  # force row vector
           p = class (p, "polynomial");
         else
           error ("@polynomial: A must be a real vector");
         endif
       endif

       superiorto ("double");

     endfunction

Note that user classes always have higher precedence than built-in Octave types.  Thus, marking the polynomial class higher than the "double" class is not actually necessary.

When confronted with two objects of equal precedence, Octave will use the method of the object that appears first in the list of arguments.

Inheritance and Aggregation
===========================

Using classes to build new classes is supported by Octave through the use of both inheritance and aggregation.

Class inheritance is provided by Octave using the class function in the class constructor.  As in the case of the polynomial class, the Octave programmer will create a structure that contains the data fields required by the class, and then call the class function to indicate that an object is to be created from the structure.  Creating a child of an existing object is done by creating an object of the parent class and providing that object as the third argument of the class function.

This is most easily demonstrated by example.  Suppose the programmer needs a FIR filter, i.e., a filter with a numerator polynomial but a denominator of 1. In traditional Octave programming this would be performed as follows.

     >> x = [some data vector];
     >> n = [some coefficient vector];
     >> y = filter (n, 1, x);

The equivalent behavior can be implemented as a class @FIRfilter.  The constructor for this class is the file FIRfilter.m in the class directory @FIRfilter.

     ## -*- texinfo -*-
     ## @deftypefn  {} {} FIRfilter ()
     ## @deftypefnx {} {} FIRfilter (@var{p})
     ## Create a FIR filter with polynomial @var{p} as coefficient vector.
     ## @end deftypefn

     function f = FIRfilter (p)

       if (nargin == 0)
         p = @polynomial ([1]);
       elseif (! isa (p, "polynomial"))
         error ("@FIRfilter: P must be a polynomial object");
       endif

       f.polynomial = [];
       f = class (f, "FIRfilter", p);

     endfunction

As before, the leading comments provide documentation for the class constructor.  This constructor is very similar to the polynomial class constructor, except that a polynomial object is passed as the third argument to the class function, telling Octave that the FIRfilter class will be derived from the polynomial class.  The FIR filter class itself does not have any data fields, but it must provide a struct to the class function.  Given that the @polynomial constructor will add an element named POLYNOMIAL to the object struct, the @FIRfilter just initializes a struct with a dummy field POLYNOMIAL which will later be overwritten.

Note that the sample code always provides for the case in which no arguments are supplied.  This is important because Octave will call a constructor with no arguments when loading objects from saved files in order to determine the inheritance structure.

A class may be a child of more than one class (see class), and inheritance may be nested.  There is no limitation to the number of parents or the level of nesting other than memory or other physical issues.

For the FIRfilter class, more control about the object display is desired.  Therefore, the display method rather than the disp method is overloaded (see Class Methods).  A simple example might be

     function display (f)
       printf ("%s.polynomial", inputname (1));
       disp (f.polynomial);
     endfunction

Note that the FIRfilter's display method relies on the disp method from the polynomial class to actually display the filter coefficients.  Furthermore, note that in the display method it makes sense to start the method with the line printf ("%s =", inputname (1)) to be consistent with the rest of Octave which prints the variable name to be displayed followed by the value.  In general it is not recommended to overload the display function.

 -- display: (OBJ)
     Display the contents of the object OBJ prepended by its name.

     The Octave interpreter calls the display function whenever it needs to present a class on-screen.  Typically, this would be a statement which does not end in a semicolon to suppress output.  For example:

          myclass (...)

     Or:

          myobj = myclass (...)

     In general, user-defined classes should overload the disp method to avoid the default output:

          myobj = myclass (...)
            => myobj =

            <class myclass>

     When overloading the display method instead, one has to take care of properly displaying the object's name.  This can be done by using the inputname function.

     disp, class, subsref, subsasgn

Once a constructor and display method exist, it is possible to create an instance of the class.  It is also possible to check the class type and examine the underlying structure.

     octave:1> f = FIRfilter (polynomial ([1 1 1]/3))
     f.polynomial = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2
     octave:2> class (f)
     ans = FIRfilter
     octave:3> isa (f, "FIRfilter")
     ans =  1
     octave:4> isa (f, "polynomial")
     ans =  1
     octave:5> struct (f)
     ans =

       scalar structure containing the fields:

     polynomial = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2

The only thing remaining to make this class usable is a method for processing data.  But before that, it is usually desirable to also have a way of changing the data stored in a class.  Since the fields in the underlying struct are private by default, it is necessary to provide a mechanism to access the fields.  The subsref method may be used for both tasks.

     function r = subsref (f, x)

       switch (x.type)

         case "()"
           n = f.polynomial;
           r = filter (n.poly, 1, x.subs{1});

         case "."
           fld = x.subs;
           if (! strcmp (fld, "polynomial"))
             error ('@FIRfilter/subsref: invalid property "%s"', fld);
           endif
           r = f.polynomial;

         otherwise
           error ("@FIRfilter/subsref: invalid subscript type for FIR filter");

       endswitch

     endfunction

The "()" case allows us to filter data using the polynomial provided to the constructor.

     octave:2> f = FIRfilter (polynomial ([1 1 1]/3));
     octave:3> x = ones (5,1);
     octave:4> y = f(x)
     y =

        0.33333
        0.66667
        1.00000
        1.00000
        1.00000

The "." case allows us to view the contents of the polynomial field.

     octave:1> f = FIRfilter (polynomial ([1 1 1]/3));
     octave:2> f.polynomial
     ans = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2

In order to change the contents of the object a subsasgn method is needed.  For example, the following code makes the polynomial field publicly writable

     function fout = subsasgn (f, index, val)

       switch (index.type)
         case "."
           fld = index.subs;
           if (! strcmp (fld, "polynomial"))
             error ('@FIRfilter/subsasgn: invalid property "%s"', fld);
           endif
           fout = f;
           fout.polynomial = val;

         otherwise
           error ("@FIRfilter/subsasgn: Invalid index type")
       endswitch

     endfunction

so that

     octave:1> f = FIRfilter ();
     octave:2> f.polynomial = polynomial ([1 2 3])
     f.polynomial = 1 + 2 * X + 3 * X ^ 2

Defining the FIRfilter class as a child of the polynomial class implies that a FIRfilter object may be used any place that a polynomial object may be used.  This is not a normal use of a filter.  It may be a more sensible design approach to use aggregation rather than inheritance.  In this case, the polynomial is simply a field in the class structure.  A class constructor for the aggregation case might be

     ## -*- texinfo -*-
     ## @deftypefn  {} {} FIRfilter ()
     ## @deftypefnx {} {} FIRfilter (@var{p})
     ## Create a FIR filter with polynomial @var{p} as coefficient vector.
     ## @end deftypefn

     function f = FIRfilter (p)

       if (nargin == 0)
         f.polynomial = @polynomial ([1]);
       else
         if (! isa (p, "polynomial"))
           error ("@FIRfilter: P must be a polynomial object");
         endif

         f.polynomial = p;
       endif

       f = class (f, "FIRfilter");

     endfunction

For this example only the constructor needs changing, and all other class methods stay the same.

classdef Classes
================

Since version 4.0, Octave has limited support for classdef classes.  In contrast to the aforementioned classes, called old style classes in this section, classdef classes can be defined within a single m-file.  Other innovations of classdef classes are:

  * access rights for properties and methods,

  * static methods, i.e., methods that are independent of an object, and

  * the distinction between value and handle classes.

Several features have to be added in future versions of Octave to be fully compatible to MATLAB.  An overview of what is missing can be found at https://wiki.octave.org/Classdef.

Creating a classdef Class
-------------------------

A very basic classdef value class (see Value Classes vs. Handle Classes) is defined by:

     classdef some_class
       properties
       endproperties

       methods
       endmethods
     endclassdef

In contrast to old style classes, the properties-endproperties block as well as the methods-endmethods block can be used to define properties and methods of the class.  Because both blocks are empty, they can be omitted in this particular case.

For simplicity, a more advanced implementation of a classdef class is shown using the polynomial example again (see Creating a Class):

     classdef polynomial2
       properties
         poly = 0;
       endproperties

       methods
         function p = polynomial2 (a)
           if (nargin == 1)
             if (isa (a, "polynomial2"))
               p.poly = a.poly;
             elseif (isreal (a) && isvector (a))
               p.poly = a(:).';  # force row vector
             else
               error ("polynomial2: A must be a real vector");
             endif
           endif
         endfunction

         function disp (p)
           a = p.poly;
           first = true;
           for i = 1 : length (a);
             if (a(i) != 0)
               if (first)
                 first = false;
               elseif (a(i) > 0 || isnan (a(i)))
                 printf (" +");
               endif
               if (a(i) < 0)
                 printf (" -");
               endif
               if (i == 1)
                 printf (" %.5g", abs (a(i)));
               elseif (abs (a(i)) != 1)
                 printf (" %.5g *", abs (a(i)));
               endif
               if (i > 1)
                 printf (" X");
               endif
               if (i > 2)
                 printf (" ^ %d", i - 1);
               endif
             endif
           endfor

           if (first)
             printf (" 0");
           endif
           printf ("\n");
         endfunction
       endmethods
     endclassdef

An object of class polynomial2 is created by calling the class constructor:

     >> p = polynomial2 ([1, 0, 1])
     => p =

      1 + X ^ 2

Properties
----------

All class properties must be defined within properties blocks.  The definition of a default value for a property is optional and can be omitted. The default initial value for each class property is [].

A properties block can have additional attributes to specify access rights or to define constants:

     classdef some_class
       properties (Access = MODE)
         PROP1
       endproperties

       properties (SetAccess = MODE, GetAccess = MODE)
         PROP2
       endproperties

       properties (Constant = true)
         PROP3 = pi ()
       endproperties

       properties
         PROP4 = 1337
       endproperties
     endclassdef

where MODE can be one of:

public
     The properties can be accessed from everywhere.

private
     The properties can only be accessed from class methods.  Subclasses of that class cannot access them.

protected
     The properties can only be accessed from class methods and from subclasses of that class.

When creating an object of some_class, PROP1 has the default value [] and reading from and writing to PROP1 is defined by a single MODE.  For PROP2 the read and write access can be set differently.  Finally, PROP3 is a constant property which can only be initialized once within the properties block.

By default, in the example PROP4, properties are not constant and have public read and write access.

 -- properties: (OBJ)
 -- properties: (CLASS_NAME)
 -- PROPLIST =: properties (...)
     Display or return the public properties for the classdef object OBJ or the named class CLASS_NAME.

     If an output value is requested, return the list of property names in a cell array.

     Programming Note: Property names are returned if the GetAccess attribute is public and if the Hidden attribute is false. methods

Methods
-------

All class methods must be defined within methods blocks.  An exception to this rule is described at the end of this subsection.  Those methods blocks can have additional attributes specifying the access rights or whether the methods are static, i.e., methods that can be called without creating an object of that class.

     classdef some_class
       methods
         function obj = some_class ()
           disp ("New instance created.");
         endfunction

         function disp (obj)
           disp ("Here is some_class.");
         endfunction
       endmethods

       methods (Access = MODE)
         function r = func (obj, r)
           r = 2 * r;
         endfunction
       endmethods

       methods (Static = true)
         function c = circumference (radius)
           c = 2 * pi () .* radius;
         endfunction
       endmethods
     endclassdef

The constructor of the class is declared in the methods block and must have the same name as the class and exactly one output argument which is an object of its class.

It is also possible to overload built-in or inherited methods, like the disp function in the example above to tell Octave how objects of some_class should be displayed (see Class Methods).

In general, the first argument in a method definition is always the object that it is called from.  Class methods can either be called by passing the object as the first argument to that method or by calling the object followed by a dot (".") and the method's name with subsequent arguments:

     >> obj = some_class ();
     New instance created.
     >> disp (obj);   # both are
     >> obj.disp ();  # equal

In some_class, the method func is defined within a methods block setting the Access attribute to MODE, which is one of:

public
     The methods can be accessed from everywhere.

private
     The methods can only be accessed from other class methods.  Subclasses of that class cannot access them.

protected
     The methods can only be accessed from other class methods and from subclasses of that class.

The default access for methods is public.

Finally, the method circumference is defined in a static methods block and can be used without creating an object of some_class.  This is useful for methods, that do not depend on any class properties.  The class name and the name of the static method, separated by a dot ("."), call this static method.  In contrast to non-static methods, the object is not passed as first argument even if called using an object of some_class.

     >> some_class.circumference (3)
     => ans =  18.850
     >> obj = some_class ();
     New instance created.
     >> obj.circumference (3)
     => ans =  18.850

Additionally, class methods can be defined as functions in a folder of the same name as the class prepended with the @ symbol (see Creating a Class).  The main classdef file has to be stored in this class folder as well.

Inheritance
-----------

Classes can inherit from other classes.  In this case all properties and methods of the superclass are inherited to the subclass, considering their access rights.  Use this syntax to inherit from superclass:

     classdef subclass < superclass
       ...
     endclassdef

Value Classes vs. Handle Classes
--------------------------------

There are two intrinsically different types of classdef classes, whose major difference is the behavior regarding variable assignment.  The first type are value classes:

     classdef value_class
       properties
         prop1
       endproperties

       methods
         function obj = set_prop1 (obj, val)
           obj.prop1 = val;
         endfunction
       endmethods
     endclassdef

Assigning an object of that class to another variable essentially creates a new object:

     >> a = value_class ();
     >> a.prop1 = 1;
     >> b = a;
     >> b.prop1 = 2;
     >> b.prop1
     => ans =  2
     >> a.prop1
     => ans =  1

But that also means that you might have to assign the output of a method that changes properties back to the object manually:

     >> a = value_class ();
     >> a.prop1 = 1;
     >> a.set_prop1 (3);
     => ans =

     <object value_class>

     >> ans.prop1
     => ans =  3
     >> a.prop1
     => ans =  1

The second type are handle classes.  Those classes have to be derived from the abstract handle class:

     classdef handle_class < handle
       properties
         prop1
       endproperties

       methods
         function set_prop1 (obj, val)
           obj.prop1 = val;
         endfunction
       endmethods
     endclassdef

In the following example, the variables a and b refer to the very same object of class handle_class:

     >> a = handle_class ();
     >> a.prop1 = 1;
     >> b = a;
     >> b.prop1 = 2;
     >> b.prop1
     => ans =  2
     >> a.prop1
     => ans =  2

Object properties that are modified by a method of an handle class are changed persistently:

     >> a.set_prop1 (3);
     >> a.prop1
     => ans =  3