Search code examples
matlaboopconstructoreval

Getting the full path of the function calling the constructor


I'd like the constructor of a class to automatically determine the full path to the calling function, so that that class can write a file that's guaranteed to be in the caller's directory (instead of whatever happens to be the pwd()).

So, I have the following setup:

In some_path/test.m:

function test    
    SomeClass()    
end

In some_path/some_subdir/SomeClass.m:

classdef SomeClass < handle    
    methods
        function obj = SomeClass()            
            evalin('caller', 'mfilename(''fullpath'')')
        end
    end    
end

When I call test(), I get the following:

>> test()
ans = 
    'some_path/some_subdir/SomeClass.m'  % <- ...why? 

I expected the call to mfilename() in evalin('caller', ...) to evaluate inside test(), but apparently, that doesn't happen...

Nesting evalins doesn't seem to help:

...
function obj = SomeClass()            
    evalin('caller', ' evalin(''caller'', ''mfilename(''''fullpath'''')'') ')
end
...

>> test()
ans = 
    'some_path/some_subdir/SomeClass.m'

The only way to get this to work is the far less intuitive dbstack():

...
function obj = SomeClass()            
    S = dbstack(1, '-completenames');
    S(1).file            
end
...

>> test()
ans = 
    'some_path/test.m'

What am I missing?


Solution

  • It looks like you cannot use evelin for this purpose. From the documentation:

    evalin('caller', expression) finds only variables in the caller's workspace; it does not find functions in the caller.

    From that I read that not the full context of the calling function is recovered before evaluating the expression, only variables in the workspace of the calling function are made available.

    That same documentation page also mentions this limitation:

    evalin cannot be used recursively to evaluate an expression. For example, a sequence of the form evalin('caller', 'evalin(''caller'', ''x'')') doesn't work.

    That is consistent with the notion that only the caller's workspace is made available, not the full context. The expression is not actually evaluated as if it were written inside the calling function.

    I have repeated your experiment with a simple M-file function, just to verify this indeed is not specific to classes or constructors, but generally applies to any function, anywhere.

    The dbstack option is the way to go.