[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
- overloaded functions vs. internals,
John W. Eaton <=