octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

overloaded functions vs. internals


From: John W. Eaton
Subject: overloaded functions vs. internals
Date: Wed, 7 Oct 2009 14:39:31 -0400

Given the following class constructor and method:

  @foo/foo.m:
  function x = foo ()
    x = class (struct (), 'foo');
  end

  @foo/size.m:
  function s = size (x)
    s = [13, 42];
  end

Octave will do the following:

  octave> x = foo ();
  octave> size (x)
  ans =

     13   42

  octave> whos x
  Variables in the current scope:

    Attr Name        Size                     Bytes  Class
    ==== ====        ====                     =====  ===== 
         x           1x1                          0  foo

The problem is that internally, whos is computing size using the
octave_value::dims function, and that does not call size, even though
the octave_value::size function has been overloaded to call a
user-defined size method if it is available.

Fixing whos to call size instead of using dims, and then construct the
dimension string from that fixes the problem for whos.  As a quick fix
for the person who reported this problem to me, I checked in the
following change:

  http://hg.savannah.gnu.org/hgweb/octave/rev/bb413c0d0d6d

However, the problem remains for any other internal function that
calls dims on a class object that has an overloaded size function.

I started working on a better fix, but ran into problems with const.
For example, I would like to replace the current octave_class::dims
function with

  dim_vector
  octave_class::dims (void) const
  {
    // Be consistent with size if it is overloaded with a user-defined
    // method.

    Matrix sz = size ();

    dim_vector dv (sz.numel ());

    for (int i = 0; i < dv.length (); i++)
      dv(i) = sz(i);

    return dv;
  }

but this won't work because size is not const (and can't be; see
below).  Dropping the const qualifier from the dims function is not
desirable, because dims is used in many other const functions, so it
would cause a cascade of const removal.  I don't think we want that.

So why isn't the octave_value::size method constant?  Because the
octave_class definition of size is

  Matrix
  octave_class::size (void)
  {
    Matrix retval (1, 2, 1.0);
    octave_value meth = symbol_table::find_method ("size", class_name ());

    if (meth.is_defined ())
      {
        count++;
        octave_value_list args (1, octave_value (this));

        octave_value_list lv = feval (meth.function_value (), args, 1);
        if (lv.length () == 1 && lv(0).is_matrix_type () && lv(0).dims 
().is_vector ())
          retval = lv(0).matrix_value ();
        else
          error ("@%s/size: invalid return value");
      }

    return retval;
  }

Note the lines

        count++;
        octave_value_list args (1, octave_value (this));

This sets up the argument list for the call to the user-defined
method.  We have to increment the reference count of the octave_class
object when we put it inside a new octave_value object so that it can
go in the argument list.  Since count is a member of
octave_base_value, and octave_class is derived from that,
octave_class::size can't be const.

My next thought was to make count mutable, but that is not sufficient
because if octave_value::size is const, then "this" is const inside
octave_value::size, and although we have an

  octave_value::octave_value (octave_base_value *)

constructor, there is no

  octave_value::octave_value (const octave_base_value *)

constructor because octave_value::rep is not const and copying the
const argument to the non-const rep won't work.  And anyway, rep can't
be const if we want to call any non-const member functions through the
rep pointer.

So, I'm a bit stuck here.  Any thoughts about how to get out of this
mess?

Ideally, I'd like to find a solution for the const problem so that we
can call overloaded methods without having to give up const.  Perhaps
there is something simple that I'm missing, but I just don't see a
solution at the moment.

Note that this problem affects much more than just dims and size.  It
is potentially trouble for any octave_value member function that is
used to implement a scripting language funtion that can be overloaded
by a user-defined method for a class object.  For example, numel, nnz,
rows, columns, etc.  Even though these work properly for overloaded
methods in the scripting language, if the corresponding octave_value
method is called internally, it will not call the overloaded function.
For example, if the function

  @foo/nnz.m:
  function n = nnz (x)
    n = 100;
  end

is added to the foo class shown above, then it will be called for

  nnz (foo ())

But, the following function shows that  the corresponding internal
octave_value::nnz funtion will not call the overloaded method:

  #include <octave/oct.h>

  DEFUN_DLD (doit, args, , "")
  {
    octave_value_list retval;

    if (args.length () == 1)
      retval(1) = args(0).nnz ();
    else
      print_usage ();

    return retval;
  }

Using this function, I see

  octave> doit (foo ())
  error: octave_base_value::nnz (): wrong type argument `class'

Comments or suggestions?

Thanks,

jwe


reply via email to

[Prev in Thread] Current Thread [Next in Thread]