Error with function handles of object methods
Jaroslav Hajek
highegg at gmail.com
Fri Jul 24 05:26:31 CDT 2009
On Fri, Jul 24, 2009 at 8:17 AM, Jaroslav Hajek<highegg at gmail.com> wrote:
> On Fri, Jul 24, 2009 at 7:12 AM, Jaroslav Hajek<highegg at gmail.com> wrote:
>> On Thu, Jul 23, 2009 at 10:45 PM, John W. Eaton<jwe at octave.org> wrote:
>>> On 24-Jun-2009, WMennerich wrote:
>>>
>>> | Hello,
>>> | I found a bug using objects:
>>> | In short words: Function handles of object functions (methods) within
>>> | the class directory (beginning with '@') can not be created.
>>> |
>>> | I use Octave V3.2.0 (mingw binary) on WindowsXP.
>>> |
>>> | I hope, this bug report contains enough details,
>>> | otherwise just ask please.
>>> |
>>> |
>>> | W.Mennerich
>>> |
>>> |
>>> |
>>> | In the following the tests I made:
>>> |
>>> |
>>> |
>>> |
>>> |
>>> | function test (<scalar>,<scalar>)
>>> | [constructor in the '@test' directory
>>> | *********************************
>>> | function newtest=test(att1, att2)
>>> | %constructor
>>> |
>>> | switch nargin
>>> | case 0
>>> | newtest.att1=[];
>>> | newtest.att2=[];
>>> | case 2
>>> | newtest.att1=att1;
>>> | newtest.att2=att1+att2;
>>> | end
>>> |
>>> | newtest=class(newtest,'test');
>>> | **********************************
>>> |
>>> |
>>> | function sumAtts(<test>)
>>> | [a method in the '@test' directory
>>> | **********************************
>>> | function res=sumAtts(this)
>>> | res=this.att1+this.att2;
>>> |
>>> |
>>> |
>>> | function get(<test>,<string>)
>>> | [for testing function overloading,
>>> | also inside '@test']
>>> | **********************************
>>> | function att=get(this,nameAtt)
>>> | att=this.(nameAtt);
>>> |
>>> |
>>> |
>>> | Test environment:
>>> | ##################################
>>> | ##################################
>>> |
>>> | * Create all files
>>> |
>>> | * Create '@test'
>>> |
>>> | * Put all files into '@test'
>>> |
>>> | * Put '@test' into octave path
>>> |
>>> |
>>> |
>>> | Running the tests:
>>> | ##################################
>>> | ##################################
>>> |
>>> | %Testing the class:
>>> | %create three objects and put them into a cell array:
>>> |
>>> | mytest1=test(33,44)
>>> | mytest2=test(66,88)
>>> | mytest3=test(132,176)
>>> |
>>> | allmytests={mytest1 mytest2 mytest3}
>>> |
>>> |
>>> | %Workspace of octave shows:
>>> |
>>> |
>>> | Attr Name Size Bytes Class
>>> | ==== ==== ==== ===== =====
>>> | allmytests 1x3 48 cell
>>> | mytest1 1x1 16 test
>>> | mytest2 1x1 16 test
>>> | mytest3 1x1 16 test
>>> |
>>> |
>>> |
>>> |
>>> |
>>> | %Testing of the sumAtts(<test>) function:
>>> |
>>> | > sumAtts(mytest1)
>>> | ans = 110
>>> |
>>> |
>>> | > sumAtts(mytest2)
>>> | ans = 220
>>> |
>>> |
>>> | > sumAtts(mytest3)
>>> | ans = 440
>>> |
>>> |
>>> |
>>> |
>>> |
>>> |
>>> | %Function 'sumAtts' as input of an anonymous function works:
>>> |
>>> | > cellfun(@(x) sumAtts(x),allmytests)
>>> | ans =
>>> |
>>> | 110 220 440
>>> |
>>> |
>>> |
>>> | %Also the (overloaded) function 'get' works fine:
>>> | > cellfun(@(x) get(x,'att1'),allmytests)
>>> | ans =
>>> |
>>> | 33 66 132
>>> |
>>> |
>>> | %Using cellfun with a sumAtts handle does not work:
>>> |
>>> | > cellfun(@sumAtts,allmytests)
>>> | error: error creating function handle "@sumAtts"
>>> | error: evaluating argument list element number 1
>>> | error: error creating function handle "@sumAtts"
>>> | error: evaluating argument list element number 1
>>> |
>>> |
>>> | %The same also with overloaded 'get' function, gives another error:
>>> |
>>> | > inStrings={'att1' 'att1' 'att1'}
>>> | inStrings =
>>> |
>>> | {
>>> | [1,1] = att1
>>> | [1,2] = att1
>>> | [1,3] = att1
>>> | }
>>> |
>>> | > cellfun(@get,allmytests,inStrings)
>>> | error: octave_base_value::array_value(): wrong type argument `class'
>>> | error: get: expecting graphics handle as first argument
>>>
>>> Jaroslav's recent changes seem to fix this problem, but
>>>
>>> cellfun(@sumAtts,allmytests)
>>>
>>> still doesn't work for me.
>>>
>>> | %Try to create a function handle outside of cellfun:
>>> |
>>> | > fun=@sumAtts
>>> | error: error creating function handle "@sumAtts"
>>>
>>> Jaroslav, is this supposed to work now?
>>>
>>> jwe
>>>
>>
>> Currently, a non-overloaded version of the function must exist, so
>> that there's always at least one function to dispatch to. Quoting
>> Matlab docs:
>> "At the time you create a function handle, the function you specify
>> must be on the MATLAB path and in the current scope."
>>
>> This seems to me to suggest that Matlab requires the same. If not,
>> then I would prefer if someone investigated what exactly the Matlab
>> requirements are.
>>
>> We can easily relax the behavior - not require the base function to
>> exist and let the handle gripe when referenced and no dispatch is
>> found. The downside is that mistyped references like @nonexistingname
>> will not give an error at the time of construction.
>> To overcome this, one would need to search all overloads, and that
>> requires something like load_path::any_method, able to query whether a
>> method with a given name exists in any class, which AFAIK isn't
>> currently there.
>>
>> Now that I think of it, this sounds like an useful extension even if
>> Matlab doesn't actually allow it, so I'll try to do something.
>>
>> regards
>>
>
> Update: using the following patch:
> http://hg.savannah.gnu.org/hgweb/octave/rev/0c7d84a65386
>
> I'm now getting:
>
> octave:1> mytest1=test(33,44)
> octave:2> mytest2=test(66,88)
> octave:3> mytest3=test(132,176)
> octave:4>
> octave:4> allmytests={mytest1 mytest2 mytest3}
> allmytests =
>
> {
> }
>
> octave:5> cellfun(@sumAtts,allmytests)
> ans =
>
> 110 220 440
>
> octave:6> inStrings={'att1' 'att1' 'att1'}
> inStrings =
>
> {
> [1,1] = att1
> [1,2] = att1
> [1,3] = att1
> }
>
> octave:7> cellfun(@get,allmytests,inStrings)
> ans =
>
> 33 66 132
>
>
> Further,
>
> octave:8> cellfun(@sumAtts,[allmytests,{1}])
> error: no sumAtts method to handle class double
> error: cellfun: too many output arguments
> octave:8> @xxx
> error: error creating function handle "@xxx"
>
> So, everything works as expected except the spurious extra error
> message from cellfun (which I'm going to look at).
>
> it would still be great if someone checked whether there are important
> differences between Matlab and Octave in this regard.
>
> cheers
>
Update:
the following patch:
http://hg.savannah.gnu.org/hgweb/octave/rev/cb0b21f34abc
fixes the extra error from cellfun.
Further, trying the following simplistic benchmark:
a(1:1e6) = {1};
tic; cellfun ("sum", a); toc
tic; cellfun (@sum, a); toc
tic; cellfun (@(x) sum (x), a); toc
I get (Core 2 Duo @ 2.83GHz, gcc -O3 -march=native) with an older build:
Elapsed time is 1.25346 seconds.
Elapsed time is 1.25844 seconds.
Elapsed time is 10.7132 seconds.
and with the recent patches:
Elapsed time is 1.16948 seconds.
Elapsed time is 1.50289 seconds.
Elapsed time is 10.1414 seconds.
so the slow-down is some 20%. If a user does not need overloading,
supplying just the name of a function as a string ignores possible
overloads, providing better speed, which is demonstrated the second
benchmark.
Of course, all this is still orders of magnitude slower than applying
some of the optimized built-in mappers like "isempty" (these also do
ignore overloads, and did so from the dawn of time, since they
directly map to internal octave_value methods).
All in all, the performance penalty seems acceptable to me (especially
considering that you can work around it). Also, this is a very
artificial example where the call itself does little work, in
real-life examples the relative penalty is probably going to be
smaller.
Of course, things may still be optimized in the future.
--
RNDr. Jaroslav Hajek
computing expert & GNU Octave developer
Aeronautical Research and Test Institute (VZLU)
Prague, Czech Republic
url: www.highegg.matfyz.cz
More information about the Bug-octave
mailing list