Basic classdef functionality

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

Basic classdef functionality

Philipp Kutin
Hi,


I'd like to put up the classdef topic to discussion again, because I
have a slightly different view on it. In my opinion, an implementation
of the bare basics would already be *tremendously* useful for
application programming (as opposed to writing a bunch of mostly
independent computational routines). The reason is that new-style
classes remedy one of the major age-old issues of the M language: the
complete inability to have reference semantics for values of composite
type, such as structs and cell arrays.

[This is mostly in reply to this thread:
http://octave.1599824.n4.nabble.com/classdef-functionality-td4417216.html
. Sorry for cross-posting, but I felt that it belonged here instead of
octave-help.]


The usual scenario when writing applications is that you have a global
variable for its state, say "global simpl". This gets initialized to a
struct on the first invocation (i.e. if it's the empty array), and
subsequently stores state both persistent across reopens of the app
window, as well as temporary state like the last position of the mouse
pointer, for example in order to be shared across various callbacks.

But the language's value passing/assignment/etc. semantics make some
constructions very unsightly, and impede code reuse among
applications. For instance, say you want to code a message bar with a
timeout: your basic structure is

  simpl.msg = struct('last','', 'keepuntil',0);

Then you write a function like "simpl_message(fmt, varargin)", which
sets simple.msg's 'last' field to the formatted message and
'keepuntil' to a serial time until which it is to be kept. But you
cannot factor out this function for other apps, e.g. "global viewer",
because it explicitly refers to "simpl"! (Well, maybe except by
resorting to "eval", but I consider this more voodoo than
programming.)


So, even a most basic set of classdef features would make it
significatly more comfortable to cope with the mutable state pattern,
as well as making life easier overall. I'm thinking of the following
ones for an initial implementation:

  * Of course, the bare minimum: classdef syntax, properties,
constructors, methods. The class *must* be inherited from "handle"
(providing reference semantics), but that is only for compatibility;
the implementation would not yet support general inheritance.
  * Access control is public by default -- stuff like
"(Access=protected)" is accepted syntax, but issuing an NYI warning.
(Maybe only once at the end of each loaded file to not spam too much.)
  * Objects need only be scalar, but should be containeable in other
composites (cells, structs, and in properties of objects themselves).
  * Destructors are definitely useful, too.

Starting from this basic set of features, one could see what else is
likely to be useful:

  * Constant, i.e. per-class properties, accessed using ClassName.MYCONST.
  * Private access control; "protected" would be the same in the
inheritance-less implementation.
  * Static methods are somewhat useful, as are secondary (helper)
functions after the "end" that closes the classdef.

And... that's pretty much everything I can come up with at this point
that has an immediately obvious use. So while the implementation would
be far from complete in the sense of supporting MATLAB features, it
would already enable a totally new style of programming applications
or modules that can be reused in many contexts. With time, these would
be completed based on actual use cases -- the less likely something
could be realized using the provided functionality (e.g. array of
objects --> cell array of objects), the more interesting it would be
as a candidate feature.

----------
Looking into the Mercurial repository, I see that there's been some
development on classdef some months ago, so maybe the current state
already supersedes my concept draft. As a token of my appreciation,
I'm considering donating 100 US$ to Michael Goffioul, who seems to be
the primary classdef implementor. It doesn't seem possible to specify
a particular person using the PayPal dialog on the Octave homepage,
though. I realize that this can hardly be considered "payment" for the
hard work everyone puts into Octave, but I hope that my little rant is
motivation in itself. Proper user-definable objects passed by
reference are a big deal.
----------

Returning to the concept once more, a couple of things come to mind
that would probably need looking into in some depth for the sake of
compatibility:

  * What expressions are allowed as property initializers? For a
start, you'd expect these to be at least everything that is a
translation-time constant, but if that's too hard to realize in full
generality, maybe a limited set would be useful. For example, nothing
(defaulting to the customary empty array), an empty cell array, and a
literal matrix / a literal string.
  * How exactly may constructors be called? In contrast to functions,
MATLAB disallows passing a constructor a smaller number of actual
arguments than are formally declared. There is no constructor
overloading. Varargin works as usual though, as far as I can see.
Thankfully, destructors have a pre-specified signature ("function
delete(o)"), so no worries there.


Are the above points realistic goals? Please apologize if they sound
too much like demands; I only want to suggest such an approach for the
better of Octave, IMO. It would be excellent to eventually have
"classdef light" in the default branch, but if Octave could be built
from the classdef branch without errors, that would be OK, too. When I
tried a week ago from a fresh clone, compilation gave me errors in the
Bison parser, if I recall correctly.

As a final motivational example, consider an interface to a user's
favorite GUI toolkit (or maybe, "library providing graphics and basic
GUI-like functionality"). The gateway to that library is realized
using a single MEX file, multiplexing some of its functionality. On
the Octave side, a GUI instance is then represented as an object of a
class, one of whose properties is an array of child GUI objects. The
beauty of this is that the GUI is entirely decomposable into its
sub-parts that can be reused individually, such as a container of
buttons belonging together.

I hope that I could convince you a little :).


Regards,
Philipp
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Richard Crozier
On 25/07/2013 12:06, Philipp Kutin wrote:

>   When I
> tried a week ago from a fresh clone, compilation gave me errors in the
> Bison parser, if I recall correctly.

You should have another go and report the specific error, I've built the
classdef branch a few times and have not usually had any build issues,
and the classdef stuff has worked really well too (thanks devs!). It
already does a lot of what you suggest I think, such as handle classes
and inheritance. Perhaps you just have a bad setup, you should try
building the default or last release version to check.

> As a final motivational example, consider an interface to a user's
> favorite GUI toolkit (or maybe, "library providing graphics and basic
> GUI-like functionality"). The gateway to that library is realized
> using a single MEX file,

Perhaps you mean something like this:

https://www.mathworks.co.uk/matlabcentral/fileexchange/38964-example-matlab-class-wrapper-for-a-c++-class

I have tried this out with the classdef branch and it works. I have a
project that has quite an involved example interface to some C++ finite
element codes here: https://sourced.ecdf.ed.ac.uk/projects/see/xfemm

I find this method of interfacing incredibly useful and am creating an
interface to Qucs (

Regards,
Richard

--
The University of Edinburgh is a charitable body, registered in
Scotland, with registration number SC005336.

Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Jordi Gutiérrez Hermoso-2
In reply to this post by Philipp Kutin
On 25 July 2013 07:06, Philipp Kutin <[hidden email]> wrote:
> I'd like to put up the classdef topic to discussion again, because I
> have a slightly different view on it.

Not to be rude, but talk is kind of cheap... We don't need to be told
what to do, we need to get it done.

> Looking into the Mercurial repository, I see that there's been some
> development on classdef some months ago, so maybe the current state
> already supersedes my concept draft. As a token of my appreciation,
> I'm considering donating 100 US$ to Michael Goffioul, who seems to
> be the primary classdef implementor.

Ah, this is definitely a little less cheap.

jwe and Michael both worked on it, but Michael's work is more recent.

> It doesn't seem possible to specify a particular person using the
> PayPal dialog on the Octave homepage, though.

That's kind of intentional, but if you want to directly give money to
Michael, nothing's stopping you. Michael, how would you like to
receive the money?

> Proper user-definable objects passed by reference are a big deal.

Everyone has a big deal with Octave. Reference semantics happen to be
your big deal.

> I hope that I could convince you a little :).

I'm not sure if Michael actually wants to work on classdef for money.
I know I personally don't want to; classdef, even the parts that seem
simple to you, seems to me like a giant mess.

But tell you what, you seem to have a clear goal of what needs to be
done. I haven't built the classdef branch in a long time, so I don't
know what the current status is. Why don't you prepare a story in
freedomsponsors or bountysource? Raise the money between many people
and find someone who can do it. Maybe Michael, maybe someone else. It
will need some work to do the advertising, but if classdef is that
important to you, perhaps you could do the advertising to raise enough
money to motivate someone to finish it for you?

- Jordi G. H.
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Philipp Kutin
In reply to this post by Richard Crozier
Hi Richard,

> On 25/07/2013 12:06, Philipp Kutin wrote:
>
>>   When I
>> tried a week ago from a fresh clone, compilation gave me errors in the
>> Bison parser, if I recall correctly.
Oops, false alarm. I forgot to install bison, assuming "apt-get
build-dep octave" would do it. Shame on me.

> Perhaps you mean something like this:
>
> https://www.mathworks.co.uk/matlabcentral/fileexchange/38964-example-matlab-class-wrapper-for-a-c++-class
>
> I have tried this out with the classdef branch and it works.
That looks pretty interesting, but I'm reluctant to use code from
MATLAB FEX for portable or Octave projects. See for example the
discussion here:
http://octave.1599824.n4.nabble.com/Terms-of-use-Mathworks-file-exchange-td4609678.html
.

If you obtained your copy from Oliver Woodford personally, could you
reupload it somewhere, maybe on Agora? (http://agora.octave.org)

-- Philipp

[NOTE: I initially replied to Richard only. Sorry for the double post!]
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Philipp Kutin
In reply to this post by Jordi Gutiérrez Hermoso-2
Hi Jordi,

On Thu, Jul 25, 2013 at 5:54 PM, Jordi Gutiérrez Hermoso
<[hidden email]> wrote:
> Not to be rude, but talk is kind of cheap... We don't need to be told
> what to do, we need to get it done.
First, as I said, I didn't intend to "tell what to do", merely suggest
a direction. Second, I disagree. Design is an essential part of the
software development process; in particular, it has to be clear what
the goals are. Hence, my mail was merely that: an attempt to suggest a
subset of the huge classdef functionality that implies the maximum
usefulness. [OK, I realize things can't always be expressed so
objectively as everyone has their own ideas.]

> That's kind of intentional, but if you want to directly give money to
> Michael, nothing's stopping you.
Not to belittle Michael's contribution, but I guess I changed my mind
to donating to the project at large. I have used various parts of
Octave in the past, but classdef seems to be very basic at this point
(or maybe Michael has a different direction in mind; in any case I'd
be curious to hear his opinion).

> Everyone has a big deal with Octave. Reference semantics happen to be
> your big deal.
Well, I like to think that classdef makes the language richer in
objective terms. But OK... point taken.

>> I hope that I could convince you a little :).
>
> I'm not sure if Michael actually wants to work on classdef for money.
I obviously didn't mean the money, but the design proposal.

> I haven't built the classdef branch in a long time, so I don't
> know what the current status is. Why don't you prepare a story in
> freedomsponsors or bountysource?
Meh. The thing is, I'd be interested in contributing code to Octave
myself, if it weren't for some "points of friction". I have good
practical experience in C, but only theoretical (a lecture) in C++.
Then, the project seems rather large, but maybe I just need to start
doing stuff (like debugging the segfault that happens when you index a
classdef object like "o()") to get familiar with it. As for the code
style, let's just assume that I wear huge rose-tinted glasses... oh
well ;).

--Philipp
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

John W. Eaton
Administrator
On 07/25/2013 02:42 PM, Philipp Kutin wrote:

> As for the code
> style, let's just assume that I wear huge rose-tinted glasses... oh
> well ;).

What is that supposed to mean?

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Michael Goffioul
In reply to this post by Philipp Kutin
On Thu, Jul 25, 2013 at 2:42 PM, Philipp Kutin <[hidden email]> wrote:
> That's kind of intentional, but if you want to directly give money to
> Michael, nothing's stopping you.
Not to belittle Michael's contribution, but I guess I changed my mind
to donating to the project at large. I have used various parts of
Octave in the past, but classdef seems to be very basic at this point
(or maybe Michael has a different direction in mind; in any case I'd
be curious to hear his opinion).

Can I ask you whether you actually tested the classdef branch? Almost all "basic" features (if not all of them) that you listed in the initial mail are already supported. As you mentioned in that initial mail, having those features would make the classdef support tremendously useful. Though in the above you say that classdef seems very basic. Can I ask you what is(are) the missing feature(s) that transform the current classdef branch from tremendously useful to very basic?

Michael.

Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Richard Crozier
In reply to this post by Philipp Kutin
On 25/07/2013 19:38, Philipp Kutin wrote:
> If you obtained your copy from Oliver Woodford personally, could you
> reupload it somewhere, maybe on Agora? (http://agora.octave.org) --
> Philipp [NOTE: I initially replied to Richard only. Sorry for the
> double post!]

Actually I have got him to send me a copy directly, and sent it to this
list, but yes, I should also upload to Agora.

Richard

--
The University of Edinburgh is a charitable body, registered in
Scotland, with registration number SC005336.

Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Philipp Kutin
Hi jwe, Michael,


On Thu, Jul 25, 2013 at 8:49 PM, John W. Eaton <[hidden email]> wrote:
> On 07/25/2013 02:42 PM, Philipp Kutin wrote:
>
>> As for the code
>> style, let's just assume that I wear huge rose-tinted glasses... oh
>> well ;).
>
>
> What is that supposed to mean?

Mostly just nitpicking about how I find the code hard to read. Sure,
it's a matter of personal preference and habituation, but I'll
explain.

First, the strange indentation. Here's a representative sample:

  if (pos == std::string::npos)
    retval = make_package (name, std::string ());
  else
    {
      std::string parent_name = name.substr (0, pos);

      retval = make_package (name, parent_name);
    }

IMO, indenting the braces halfway to the code of its surrounded block
effectively doubles the number of (imaginged) vertical lines, but adds
no information. Furthermore, the code in the "if" branch and the
"else" code are now indented differently, even they're at the same
"level"!

Then there's the curious spacing, for example:

  if (! arr.xelem (i).ok ())

For my taste, the first is right, but the three following are too
much. Personally, I tend to having spaces between tokens that are
connected with operators of low precedence. I mean, you wouldn't write
"a+b * c+d" either.

But as I said, it's all preference... in fact, I could easily nitpick
about certain stylistic aspects of some code which I otherwise
consider extraordinarily well-engineered software (LuaJIT,
http://luajit.org/).


On Thu, Jul 25, 2013 at 9:09 PM, Michael Goffioul
<[hidden email]> wrote:
> Can I ask you whether you actually tested the classdef branch? Almost all
> "basic" features (if not all of them) that you listed in the initial mail
> are already supported. As you mentioned in that initial mail, having those
> features would make the classdef support tremendously useful. Though in the
> above you say that classdef seems very basic. Can I ask you what is(are) the
> missing feature(s) that transform the current classdef branch from
> tremendously useful to very basic?

At the time I wrote the reply, I had tested it for about ten minutes.
The apparent change of opinion (it's not) was partly due to too high
expectations, but also because I made a mistake: for testing, I
renamed an existing class M file and renamed the class name. But sure
enough, I forgot to rename the constructor and wondered why its code
wouldn't execute. Ouch! I will try to provide a more careful analysis
when I have played around with classdef a bit more.


--Philipp
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

John W. Eaton
Administrator
On 07/26/2013 02:54 AM, Philipp Kutin wrote:

> IMO, indenting the braces halfway to the code of its surrounded block
> effectively doubles the number of (imaginged) vertical lines, but adds
> no information. Furthermore, the code in the "if" branch and the
> "else" code are now indented differently, even they're at the same
> "level"!

So you are objecting to the GNU coding style.  Is this the first time
you've encountered it?

jwe
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Daniel Kraft
On 07/26/2013 11:48 AM, John W. Eaton wrote:

> On 07/26/2013 02:54 AM, Philipp Kutin wrote:
>
>> IMO, indenting the braces halfway to the code of its surrounded block
>> effectively doubles the number of (imaginged) vertical lines, but adds
>> no information. Furthermore, the code in the "if" branch and the
>> "else" code are now indented differently, even they're at the same
>> "level"!
>
> So you are objecting to the GNU coding style.  Is this the first time
> you've encountered it?

Sorry for the probably off-topic post, but since I first saw the GNU
style, it seems like the only "logical" option to me.  Before, I used the

if (something)
{
  body
}

style (don't remember how it is called) since I find putting the { on
the same line as if's or for's just looks absolutely bad.  With the
GNU-style you have basically just one single rule:

Everything that is "part" of some other construct is indented by some
amount (two spaces to be precise, but that doesn't matter for the basic
philosophy).

This is then consistently applied both to constructs like if/for *as
well as* for blocks.  Thus if you have a block, it will always look like

{
  body
}

because the body is "part of" the block.  And if you have an if, it will be

if (something)
  body
else
  other body

where body and "other body" are the parts.  If the body happens to be a
block, you get out what is the characteristic indention of the GNU
style.  And if "other body" happens to be a single line instead, you get
what was described above as "confusing" because both codes are then
indented differently--but this is consistent because both codes are
actually on *different* levels, since the body is wrapped into a block
in addition.

Yours,
Daniel

--
http://www.domob.eu/
--
Done:  Arc-Bar-Cav-Hea-Kni-Ran-Rog-Sam-Tou-Val-Wiz
To go: Mon-Pri
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Jordi Gutiérrez Hermoso-2
In reply to this post by Philipp Kutin
On 26 July 2013 02:54, Philipp Kutin <[hidden email]> wrote:
> Then there's the curious spacing, for example:

You should note that the spacing between function calls and () is
significant. We don't use spacing when using () for indexing, but we
do use it for function calls.

The GNU C style is somewhat reminiscent of Lisp style.

By the way, for hacking on Octave, proficiency in C is not very
helpful, and I daresay, it may even interfere. We avoid pretty much
all the awful parts of C in Octave whenever possible. C++ is a
different language that only for marketing purposes includes most but
not all of C.

- Jordi G. H.
Reply | Threaded
Open this post in threaded view
|

Re: Basic classdef functionality

Philipp Kutin
On Fri, Jul 26, 2013 at 11:48 AM, John W. Eaton <[hidden email]> wrote:
> So you are objecting to the GNU coding style.  Is this the first time
> you've encountered it?
It is, but I knew of its existence.

On Fri, Jul 26, 2013 at 3:14 PM, Jordi Gutiérrez Hermoso
<[hidden email]> wrote:
> By the way, for hacking on Octave, proficiency in C is not very
> helpful, and I daresay, it may even interfere. (...) C++ is a
> different language that only for marketing purposes includes most but
> not all of C.
Agreed! I cringe a little every time I see someone write "C/C++" like
if they were somehow similar for idiomatic code.

----------

I guess it's logical that I start with smallish changes. I posted two patches,

 #8134 Speed up waitbar () by caching axes and patch handles in
figure's __guidata__.
 #8133 profexplore.m: Allow calling with no args, add "quit" as alias
to "exit".

and three bugs,

 #39588 waitbar/gnuplot init issue: waitbar(x, han) opens two
additional figures.
 #39587 Assertion failure with profexplore after profile erroneous
use: Assertion `active_fcn != call_tree' failed.
 #39586 Assertion failure with profexplore valid use: Assertion
`fcn_id == fcn' failed.

 on the respective trackers.

The patches are on a brach I created locally, "classdef-pk", although
I verified that the files had no changes with the default branch. Is
this a problem? If you decide to apply them, it would probably be on
default then. Hopefully it will be easy to manage this somehow. (E.g.
if you decide to slightly tweak the patches, I'll have to delete my
commits and replace them with those coming from default, etc.)

--Philipp