MATLAB Answers

Michael
1

How can I get the function handle to the currently running function without relying upon the current path settings?

Asked by Michael
on 17 Apr 2013
Latest activity Edited by Image Analyst
on 7 Oct 2014
The question says it all, but here it is again. How can I get the function handle to the currently running function without relying upon the current path settings?
function myFunction();
fxn = @myFunction;
doesn't work because myFunction is shadowed by a myFunction function which isn't the current function, so fxn is a handle to the wrong myFunction.
function myFunction();
fxn = str2func(mfilename());
fails for the same reason.
function myFunction();
fxn = str2func(mfilename('fullpath'));
is not a valid way to call str2func.
Any ideas?
P.S. - For the curious, I'm trying to run a unit test on a recursive function. To create mockup functions, I have a special path full of mockup functions which is added to the path after I get the function handle to the function which I'm testing. This allows functions within the function being tested to be replaced with mockups that simplify testing. However, this behavior with a recursive function actually prevents the test from being very useful.

  1 Comment

Could you put the mockup functions in a package instead of adding the paths?

Sign in to comment.

3 Answers

Answer by Andy Campbell on 7 Oct 2014
 Accepted Answer

One approach that you can take is to exploit the fact that the local functions have higher precedence than the functions that may be found on the path. Thus you should just be able to introduce one layer of function indirection in order to always dispatch to your recursive algorithm. So, instead of thi:
function out = myFunction(in)
stuff = doStuff;
out = myFunction(stuff);
end
It would look like this:
function out = myFunction(in)
out = myLocalFunction(in);
end
function out = myLocalFunction(in)
stuff = doStuff;
out = myLocalFunction(stuff);
end
This seems like it would work for you and be much cleaner and more efficient than path or current folder manipulation. However, I am confused because it seems like you want to dispatch always to the production myFunction so why is there a test double on the path at all? I guess I don't see why it is a problem because you don't seem to ever want to dispatch to the test double at all. Am I missing something?

  1 Comment

This was a long time ago, but I think what you've suggested would work.
To further explain, we have a lot of functions in our project, and each function has a associated unit test. We use the Matlab xUnit Test Framework. In order to provide mockup functions in our unit tests, our setup() method always looks like
function result = setup()
% Get function handle (in case mockup exists).
result.function = @myFunctionToBeTested;
% Add mockups to path.
result.previousMatlabPath = addpath(genpath('mockups'));
This allows the unit test on myFunctionToBeTested to have its subfunctions replaced with mockups. But when myFunctionToBeTested is recursive, it replaces the calls to itself with the mockup, defeating the purpose of its own unit tests.
We can't just not add the mockup path in a recursive function's unit test, as such recursive functions may depend upon other functions that we do want to replace with mockups. And creating separate paths to groups of mockup functions used solely by the unit test in question becomes unmanageable, practically speaking.
The solution I used works (see comment from me on April 23, 2013), but is obviously inefficient. I think, but have not tried, that using a local function for the recursion would do the trick. Thanks for that idea!
Good luck to others who are experiencing this same issue!

Sign in to comment.


Answer by Roshin Kadanna Pally on 17 Apr 2013

Something to try:
You can determine this apriori and pass it as one of the input arguments to myFunction or save it in a mat file and load it back in your function.
fxn = @myFunction;
Execute above for the function you would be running beforehand. This need to be done only once and you can avoid shadowing by Cd-ing to the correct location.
function myFunction(fxn)
% fxn will always point to the currently running function
% Or, load a mat file that has fxn saved

  3 Comments

Thanks for the suggestions, Roshin. I agree that these methods (pass in function handle, or load from MAT file) will work, but they are a bit klunky, as they cause an unnecessary dependency which is only needed for the unit test. You also reminded me, however, that I could do the following:
function myFunction()
activeDir = cd();
cd(fileparts(mfilename('fullpath')));
fxn = @myFunction;
cd(activeDir);
It's not super efficient, especially for a recursive function, but it should work. I'm surprised that there isn't a more direct way, something like the equivalent of the 'this' keyword for an object in C++.
So you don't need the workspace of the current instantiation of the function?
I'm not sure exactly what you mean. I'm not trying to import a workspace, I'm just trying to call a function recursively. Outside of the unit test, this works just fine without using a function handle. But in the unit test, the path is altered, so the function name points first to a mock function, which does nothing. Therefore, I need to use a function handle to the original function.
In case others have this same problem, this can be made slightly more efficient by comparing the outputs of mfilename and which.
function myFunction()
thisFilePath = [mfilename('fullpath') '.m'];
if strcmp(thisFilePath, which('myFunction'))
fxn = @myFunction;
else
activeDir = cd();
cd(fileparts(thisFilePath));
fxn = @myFunction;
cd(activeDir);
end
% rest of the function would occur below including recursive call via function handle.

Sign in to comment.


Answer by Image Analyst
on 7 Oct 2014

Why can't you simply call dbstack() to get the currently running function?

  2 Comments

This doesn't return anything more useful than I can get from the function mfilename (see my comment on April 23, 2013), and, as far as I can tell, doesn't help solve the original problem.
mfilename is only the name of the m-file. dbstack gives the complete list of all the functions that have been called. So if main.m called fun1, which called fun2 which called fun3, dbstack would give you all of that (main->fun1->fun2->fun3) while mfilename could only give main.m. Though I don't think it's in the form of a "function handle."

Sign in to comment.