How to wrap an already existing function with a ne

2019-01-15 09:28发布

问题:

Is it possible to create a wrapper around a function that has the exact same name as the original function?

This would be very useful in circumstances where the user wants to do some additional checks on input variables before they are passed on to the built in function How to interrupt MATLAB IDE when it hangs on displaying very large array?

回答1:

Actually alternatively to slayton's answer you don't need to use openvar. If you define a function with the same name as a matlab function, it will shadow that function (i.e. be called instead).

To then avoid recursively calling your own function, you can call the original function from within the wrapper by using builtin.

e.g.

outputs = builtin(funcname, inputs..);

Simple example, named rand.m and in the matlab path:

function out = main(varargin)
disp('Test wrapping rand... calling rand now...');
out = builtin('rand', varargin{:});

Note that this only works for functions that are found by builtin. For those that are not, slayton's approach is likely necessary.



回答2:

Yes this is possible but it requires a bit of hacking. It requires that you copy around some function handles.

Using the example provided in the question I will show how to wrap the function openvar in a user defined function that checks the size of the input variable and then allows the user to cancel any open operation for variables that are too large.

Additionally, this should work when the user double clicks a variable in the Workspace pane of the Matlab IDE.

We need to do three things.

  1. Get a handle to the original openvar function
  2. Define the wrapper function that calls openvar
  3. Redirect the original openvar name to our new function.

Example Function

function openVarWrapper(x, vector)

    maxVarSize = 10000;
    %declare the global variable
    persistent openVarHandle; 

    %if the variable is empty then make the link to the original openvar
    if isempty(openVarHandle)
        openVarHandle = @openvar;
    end

    %no variable name passed, call was to setup connection
    if narargin==0
        return;
    end


    %get a copy of the original variable to check its size
    tmpVar = evalin('base', x);        

    %if the variable is big and the user doesn't click yes then return
    if prod( size( tmpVar)) > maxVarSize
        resp = questdlg(sprintf('Variable %s is very large, open anyway?', x));
        if ~strcmp(resp, 'Yes')
            return;
        end
    end

    if ischar(x) && ~isempty(openVarHandle);
        openVarHandle(x);
     end
 end

Once this function is defined then you simply need to execute a script that

  • Clears any variables named openvar
  • run the openVarWrapper script to setup the connection
  • point the original openVar to openVarWrapper

Example Script:

clear openvar;
openVarWrapper;
openvar = @openVarWrapper;

Finally when you want to clean everything up you can simply call:

clear openvar;


回答3:

I prefer jmetz's approach using builtin() when it can be applied, because it is clean and to the point. Unfortunately, many many functions are not found by builtin().

I found that I was able to wrap a function using a combination of the which -all and cd commands. I suspect that this approach can be adapted to a wide variety of applications.

In my example case, I wanted to (temporarily) wrap the interp1 function so that I could check for NaN output values. (The interp1 function will, by default, return a NaN under some conditions, such as if a query point is larger than the largest sample point.) Here's what I came up with:

function Vq = interp1(varargin)

   persistent interp1_builtin;

   if (isempty(interp1_builtin)) % first call: handle not set
      toolbox = 'polyfun';

      % get a list of all known instances of the function, and then 
      % select the first such instance that contains the toolbox name
      % in its path

      which_list = which('interp1','-all');
      for ii = 1:length(which_list)
         if (strfind(which_list{ii}, [filesep, toolbox, filesep]))
            base_path = fileparts(which_list{ii}); % path to the original function
            current_path = pwd;
            cd(base_path); % go to the original function's directory
            interp1_builtin = @interp1; % create a function handle to the original function
            cd(current_path); % go back to the working directory
            break
         end
      end
   end

   Vq = interp1_builtin(varargin{:});  % call the original function

   % test if the output was NaN, and print a message
   if (any(isnan(Vq)))
      dbstack;
      disp('ERROR: interp1 returned a NaN');
      keyboard
   end

end

See also: How to use MATLAB toolbox function which has the same name of a user defined function