5

Is is possible to replace the following code with something which does not use exceptions? The handle x is a provided handle. I want to test it for validity (having actual code to back the handle) before use.

x = @notreallyafunction;
try
   x();
catch 
   disp('Sorry function does not exist.');
end
chappjc
  • 30,359
  • 6
  • 75
  • 132
Andrej Mikulik
  • 569
  • 5
  • 14

1 Answers1

8

To test function handles such as for screening out the bogus x=@notreallyafunction in your question, you can use the functions command to check the handle and get the referenced function's name, type (simple, nested, overloaded, anonymous, etc.), and location if it is defined in a file.

>> x = @notreallyafunction;
>> functions(x)
ans = 
    function: 'notreallyafunction'
        type: 'simple'
        file: ''
>> x = @(y) y;
>> functions(x)
ans = 
     function: '@(y)y'
         type: 'anonymous'
         file: ''
    workspace: {[1x1 struct]}
>> 

The output of functions for a handle to a builtin (e.g. x=@round) will look just like a bogus function handle (type is 'simple'). The next step is to test the named function for existence:

>> x = @round;
>> fx = functions(x)
fx = 
    function: 'round'
        type: 'simple'
        file: ''
>> exist(fx.function)
ans =
     5
>> x = @notreallyafunction;
>> fx = functions(x)
fx = 
    function: 'notreallyafunction'
        type: 'simple'
        file: ''
>> exist(fx.function)
ans =
     0

However, you need to deal with anonymous functions since they fail existence test:

>> x = @(y) y;
>> fx = functions(x)
>> exist(fx.function)
ans =
     0

The solution is to first check the type. If type is 'anonymous', then the check passes. If the type is not 'anonymous', they you can rely on exist to check the function's validity. Summing up, you could create a function like this:

% isvalidhandle.m Test function handle for a validity.
%   For example,
%     h = @sum; isvalidhandle(h) % returns true for simple builtin
%     h = @fake; isvalidhandle(h) % returns false for fake simple
%     h = @isvalidhandle; isvalidhandle(h) % returns true for file-based
%     h = @(x)x; isvalidhandle(h) % returns true for anonymous function
%     h = 'round'; isvalidhandle(h) % returns true for real function name
%   Notes:  The logic is configured to be readable, not compact.
%           If a string refers to an anonymous fnc, it will fail, use handles.
function isvalid = isvalidhandle(h)

if ~(isa(h,'function_handle') || ischar(h)),
    isvalid = false;
    return;
end

if ischar(h)
    if any(exist(h) == [2 3 5 6]),
        isvalid = true;
        return;
    else
        isvalid = false;
        return;
    end
end

fh = functions(h);

if strcmpi(fh.type,'anonymous'),
    isvalid = true;
    return;
end

if any(exist(fh.function) == [2 3 5 6])
    isvalid = true;
else
    isvalid = false;
end
chappjc
  • 30,359
  • 6
  • 75
  • 132
  • No, this is incorrect. It returns true. I checked is* function but haven't found anything useful. – Andrej Mikulik Oct 10 '13 at 23:34
  • Hmm, still does not work... y=@sum; functions(x) returns the same result as functions(y)... you cannot distinguish – Andrej Mikulik Oct 11 '13 at 00:20
  • True, a handle to a builtin will look the same as a bogus function handle (both `type` of `simple`). However, what you can do now is test if the function in `.functions` `exist`s. – chappjc Oct 11 '13 at 00:25
  • @Dennis Jaheruddin - You call if for a function handle like `x=@str2num; isvalidhandle(x)`. As the name implies, it is for function **handles** like the OP requested. I'm sorry that wasn't clear. – chappjc Oct 11 '13 at 15:20
  • @Dennis Jaheruddin - I just added function name input handling as well... to satisfy the hard to satisfy. ;) – chappjc Oct 11 '13 at 15:56
  • 1
    @chappjc I guess I was doing something wrong last time as it seemed like my solution passed the test and yours didn't. Your enhanced explanation has turned my vote! – Dennis Jaheruddin Oct 14 '13 at 08:27