changeset 23409:907f8c3e1c8d

doc: new section about classdef classes with example (bug #44590). * doc/interpreter/oop.txi: new documentation about classdef classes including their creation, properties, methods, inheritance, and the difference between value and handle classes. * examples/code/polynomial2.m: new example to illustrate the classdef version of @polynomial. * examples/module.mk: add entry for new example class. Pushed and modified patch (file #40225) by Kai T. Ohlhus <k.ohlhus@gmail.com>.
author Markus Mützel <markus.muetzel@gmx.de>
date Wed, 19 Apr 2017 01:22:23 +0200
parents 0af9a1ae0912
children 705361dfe353
files doc/interpreter/oop.txi examples/code/polynomial2.m examples/module.mk
diffstat 3 files changed, 418 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/oop.txi	Tue Apr 18 11:59:24 2017 -0700
+++ b/doc/interpreter/oop.txi	Wed Apr 19 01:22:23 2017 +0200
@@ -4,14 +4,14 @@
 @c This file is part of Octave.
 @c
 @c Octave is free software; you can redistribute it and/or modify it
-@c under the terms of the GNU General Public License as published by
-@c the Free Software Foundation; either version 3 of the License, or
-@c (at your option) any later version.
+@c under the terms of the GNU General Public License as published by the
+@c Free Software Foundation; either version 3 of the License, or (at
+@c your option) any later version.
 @c
-@c Octave is distributed in the hope that it will be useful, but
-@c WITHOUT ANY WARRANTY; without even the implied warranty of
-@c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-@c GNU General Public License for more details.
+@c Octave is distributed in the hope that it will be useful, but WITHOUT
+@c ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+@c FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+@c for more details.
 @c
 @c You should have received a copy of the GNU General Public License
 @c along with Octave; see the file COPYING.  If not, see
@@ -41,6 +41,7 @@
 * Indexing Objects::
 * Overloading Objects::
 * Inheritance and Aggregation::
+* classdef Classes::
 @end menu
 
 @node Creating a Class
@@ -752,3 +753,356 @@
 
 For this example only the constructor needs changing, and all other class
 methods stay the same.
+
+@node classdef Classes
+@section @code{classdef} Classes
+
+Since version 4.0, Octave has limited support for @code{classdef} classes.  In
+contrast to the aforementioned classes, called @dfn{old style classes} in this
+section, @code{classdef} classes can be defined within a single m-file.  Other
+innovations of @code{classdef} classes are:
+
+@itemize @bullet
+@item
+@b{access rights} for properties and methods,
+
+@item
+@b{static methods}, i.e. methods that are independent of an object, and
+
+@item
+the distinction between @b{value and handle classes}.
+@end itemize
+
+Several features have to be added in future versions of Octave to be fully
+compatible to @sc{matlab}.  An overview of what is missing can be found at
+@url{http://wiki.octave.org/Classdef}.
+
+@menu
+* Creating a classdef Class::
+* Properties::
+* Methods::
+* Inheritance::
+* Value Classes vs. Handle Classes::
+@end menu
+
+@node Creating a classdef Class
+@subsection Creating a @code{classdef} Class
+
+A very basic @code{classdef} value class
+(@pxref{Value Classes vs. Handle Classes}) is defined by:
+
+@example
+@group
+classdef some_class
+  properties
+  endproperties
+
+  methods
+  endmethods
+endclassdef
+@end group
+@end example
+
+
+
+In contrast to old style classes, the @code{properties}-@code{endproperties}
+block as well as the @code{methods}-@code{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 @code{classdef} class is
+shown using the @code{polynomial} example again (@pxref{Creating a Class}):
+
+@example
+@group
+@EXAMPLEFILE(polynomial2.m)
+@end group
+@end example
+
+@noindent
+An object of class @code{polynomial2} is created by calling the class
+constructor:
+
+@example
+>> p = polynomial2 ([1, 0, 1])
+@result{} p =
+
+ 1 + X ^ 2
+@end example
+
+@node Properties
+@subsection Properties
+
+All class properties must be defined within @code{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 @code{[]}.
+
+A @code{properties} block can have additional attributes to specify access
+rights or to define constants:
+
+@example
+@group
+classdef some_class
+  properties (Access = @var{mode})
+    @var{prop1}
+  endproperties
+
+  properties (SetAccess = @var{mode}, GetAccess = @var{mode})
+    @var{prop2}
+  endproperties
+
+  properties (Constant = true)
+    @var{prop3} = pi ()
+  endproperties
+
+  properties
+    @var{prop4} = 1337
+  endproperties
+endclassdef
+@end group
+@end example
+
+@noindent
+where @var{mode} can be one of:
+
+@table @code
+@item public
+The properties can be accessed from everywhere.
+
+@item private
+The properties can only be accessed from class methods.  Subclasses of that
+class cannot access them.
+
+@item protected
+The properties can only be accessed from class methods and from subclasses
+of that class.
+@end table
+
+When creating an object of @code{some_class}, @var{prop1} has the default
+value @code{[]} and reading from and writing to @var{prop1} is defined by
+a single @var{mode}.  For @var{prop2} the read and write access can be set
+differently.  Finally, @var{prop3} is a constant property which can only be
+initialized once within the @code{properties} block.
+
+By default, in the example @var{prop4}, properties are not constant and have
+public read and write access.
+
+@node Methods
+@subsection Methods
+
+All class methods must be defined within @code{methods} blocks.  An exception
+to this rule is described at the end of this subsection.  Those @code{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.
+
+@example
+@group
+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 = @var{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
+@end group
+@end example
+
+The constructor of the class is declared in the @code{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
+@code{disp} function in the example above to tell Octave how objects of
+@code{some_class} should be displayed (@pxref{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
+("@code{.}") and the method's name with subsequent arguments:
+
+@example
+>> obj = some_class ();
+New instance created.
+>> disp (obj);   # both are
+>> obj.disp ();  # equal
+@end example
+
+In @code{some_class}, the method @code{func} is defined within a @code{methods}
+block setting the @code{Access} attribute to @var{mode}, which is one of:
+
+@table @code
+@item public
+The methods can be accessed from everywhere.
+
+@item private
+The methods can only be accessed from other class methods.  Subclasses of that
+class cannot access them.
+
+@item protected
+The methods can only be accessed from other class methods and from subclasses
+of that class.
+@end table
+
+@noindent
+The default access for methods is @code{public}.
+
+Finally, the method @code{circumference} is defined in a static @code{methods}
+block and can be used without creating an object of @code{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 ("@code{.}"), 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 @code{some_class}.
+
+@example
+@group
+>> some_class.circumference (3)
+@result{} ans =  18.850
+>> obj = some_class ();
+New instance created.
+>> obj.circumference (3)
+@result{} ans =  18.850
+@end group
+@end example
+
+Additionally, class methods can be defined as functions in a folder of the same
+name as the class prepended with the @samp{@@} symbol
+(@pxref{Creating a Class}).  The main @code{classdef} file has to be stored in
+this class folder as well.
+
+@node Inheritance
+@subsection 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 @code{superclass}:
+
+@example
+@group
+classdef subclass < superclass
+  @dots{}
+endclassdef
+@end group
+@end example
+
+@node Value Classes vs. Handle Classes
+@subsection Value Classes vs. Handle Classes
+
+There are two intrinsically different types of @code{classdef} classes, whose
+major difference is the behavior regarding variable assignment.  The first type
+are @b{value classes}:
+
+@example
+@group
+classdef value_class
+  properties
+    prop1
+  endproperties
+
+  methods
+    function obj = set_prop1 (obj, val)
+      obj.prop1 = val;
+    endfunction
+  endmethods
+endclassdef
+@end group
+@end example
+
+@noindent
+Assigning an object of that class to another variable essentially creates a new
+object:
+
+@example
+@group
+>> a = value_class ();
+>> a.prop1 = 1;
+>> b = a;
+>> b.prop1 = 2;
+>> b.prop1
+@result{} ans =  2
+>> a.prop1
+@result{} ans =  1
+@end group
+@end example
+
+But that also means that you might have to assign the output of a method that
+changes properties back to the object manually:
+
+@example
+@group
+>> a = value_class ();
+>> a.prop1 = 1;
+>> a.set_prop1 (3);
+@result{} ans =
+
+<object value_class>
+
+>> ans.prop1
+@result{} ans =  3
+>> a.prop1
+@result{} ans =  1
+@end group
+@end example
+
+The second type are @b{handle classes}.  Those classes have to be derived from
+the abstract @code{handle} class:
+
+@example
+@group
+classdef handle_class < handle
+  properties
+    prop1
+  endproperties
+
+  methods
+    function set_prop1 (obj, val)
+      obj.prop1 = val;
+    endfunction
+  endmethods
+endclassdef
+@end group
+@end example
+
+In the following example, the variables @code{a} and @code{b} refer to the
+very same object of class @code{handle_class}:
+
+@example
+@group
+>> a = handle_class ();
+>> a.prop1 = 1;
+>> b = a;
+>> b.prop1 = 2;
+>> b.prop1
+@result{} ans =  2
+>> a.prop1
+@result{} ans =  2
+@end group
+@end example
+
+Object properties that are modified by a method of an handle class are changed
+persistently:
+
+@example
+@group
+>> a.set_prop1 (3);
+>> a.prop1
+@result{} ans =  3
+@end group
+@end example
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/code/polynomial2.m	Wed Apr 19 01:22:23 2017 +0200
@@ -0,0 +1,56 @@
+classdef polynomial2
+  properties
+    poly = 0
+  endproperties
+
+  methods
+    function p = polynomial2 (a)
+      if (nargin > 1)
+        print_usage ();
+      endif
+
+      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
--- a/examples/module.mk	Tue Apr 18 11:59:24 2017 -0700
+++ b/examples/module.mk	Wed Apr 19 01:22:23 2017 +0200
@@ -55,6 +55,7 @@
   examples/code/oregonator.cc \
   examples/code/oregonator.m \
   examples/code/paramdemo.cc \
+  examples/code/polynomial2.m \
   examples/code/standalone.cc \
   examples/code/standalonebuiltin.cc \
   examples/code/stringdemo.cc \