Within a function : get complete command-line calling text, a la dbstack()..?

Hi everyone,
I understand how to use dbstack(), within a saved M-function, to get the complete text of the command which called the current function. For example, if we have two M-files, A.m & B.m, where A() calls (B):
FILE 'A.m'
function output = A(input)
output = B(input);
FILE 'B.m'
function output = B(input)
% ---------------------------------------------------------------------
% What line of text called this function..?
% ---------------------------------------------------------------------
% Get call-stack info:
stDebug = dbstack;
% We know a-priori that this function's caller info is in (stDebug)(2)
% Get caller file name & line number of code which called this function:
callerFileName = stDebug(2).file;
callerLineNumber = stDebug(2).line;
% Open caller file:
fCaller = fopen(callerFileName);
% Iterate through lines to get to desired line number:
for iLine = 1 : callerLineNumber
% Read current line of text:
currLine = fgetl(fCaller);
end
% (currLine) now reflects calling desired code: display this code:
fprintf('Complete text of calling code is : ''%s''\n',currLine);
% Close caller file:
fclose(fCaller);
% ---------------------------------------------------------------------
% Determine function output
% ---------------------------------------------------------------------
% Arbitrary operation just for demonstration:
output = input + 5;
If we call A() from the command-line, we get:
>> A(10)
Complete text of calling code is : 'output = B(input);'
ans = 15
However, if we call B() directly from the command-line, we get an error, b/c there is no 2nd element in the dbstack() output. In other words, for this case dbstack() output is a single-element structure containing info for the local function B() context only:
>> B(10)
Index exceeds matrix dimensions.
Error in B (line 12)
callerFileName = stDebug(2).file;
My problem : I need a way to programmatically get the complete literal calling text, within function B(), no matter whether the caller is another M-file or the command-line.
I thought of looking in the MATLAB diary file, but apparently it's not guaranteed the last line of the diary text file will reflect the currently-executing command-line command at any given point in the executing call stack..
Any suggestions greatly appreciated!
Thanks, Brad

1 Comment

This isn't an answer to the question as it won't work for command line calls, but its worth noting that there are ways to get a calling line without scanning the file the calling line is in.
A pretty simple way is to create an error, and then use the getReport function on the MatlabException object.
try
error('DummyError');
catch ME
callStackDetails = getReport(ME);
end
callLine = regexp(callStackDetails,'(?<=Error in [^\n]*\n)[^\n]*','match','once');
callLine = strtrim(callLine);

Sign in to comment.

 Accepted Answer

I was able to find a solution, with help from Google, by searching for : matlab "command history". After all that tedious debate above, it's nice to see a four-line solution, which I've verified with limited testing. Note that it relies completely on undocumented MATLAB functionality.
Sincere thanks to StackOverflow.com for this solution. Hope it can help s/o else as well..
function str = getLastMATLABcommandLineStr()
% Function returns a string containing the last command entered in the Matlab Command Window..
% Code via : http://stackoverflow.com/questions/5053692/how-do-i-search-through-matlab-command-history
% Get session history from Java object:
history = ...
com.mathworks.mlservices.MLCommandHistoryServices.getSessionHistory;
% Convert to a vector of char strings:
historyText = char(history);
% Convert to a cell vector of strings:
cvHistory = cellstr(historyText);
% Extract last string:
str = cvHistory{end};
end

More Answers (2)

Sorry for this not being an direct answer:
There are very likely cleaner methods than parsing the contents of the calling line. The input can be a function, which has side-effects such that a textual pasing will not be successful. This method will fail if called from a Mex function or a P-coded fail also.
Reading the program text of the caller is another variant of the old EVAL problems. Therefore I strongly recommend to find a more direct solution, which si safer, cleaner and is less prone to errors.

7 Comments

Hi Jan,
Thanks for your comment. I completely understand the analogy you make with eval(). So just to clarify, are you categorically stating that there are *no* legitimate contexts where the MATLAB coder should want to get the program text of the caller or to use eval()..? This would seem to be a extreme POV, no..?
Let me please illustrate with my present coding challenge: I have highly complex data structures which are used as inputs to multi-argument functions, whose outputs are passed to other multi-argument functions, etc. In order to keep very precise track of the origin of any given data structure, I developed a classdef with methods to "register" its function call origin(s) as standardized field struct(s) & sub-fields. Data structures inherit from this registration class.
So for example, at the command line we can easily execute the following:
>>data1 = functionA(1,2,3);
>>registrationStr = 'data1 = functionA(1,2,3)';
>>data1 = data1.RegisterOriginFunction(s);
>>data1.displayOrigin
ans =
outputName: 'data1'
functionName: 'functionA'
arg1 : 1
arg2 : 2
arg3 : 3
But if we would like to programmatically register the origin function, for example within createFunctionA(), we run into the problem that I described in my original question above.
Any further comments appreciated,
Thanks,
Brad
Yes, it is an extrem point of view. EVAL and the direct access of the source code is meta-programming, see e.g. http://en.wikipedia.org/wiki/Self-modifying_code
It is possible in theory and practize to do this in MATLAB, becaue all needed procedures are primitive recursive. But MATLAB has only very limited methods to control and debug such techniques. The complexity of self-referencing code grows faster than exponential. If your registration function is valid and powerful, it accepts this:
data1 = functionA(functionA(evalin('base', 'functionA(1,2,3)'), 2, 3), 2, 3);
And if the output of this is realy useful for your program, I admit that I've underestimated your programming skills.
Does this level of complexity really required to achieve the results you want to get?
Hi Jan,
Thanks for your further comments. I agree, what I'm doing is meta-programming; but I would argue it's really the most basic kind & doesn't really introduce exponential-plus complexity. Please keep in mind that I'm only querying at the meta-level & not self-modifying in any way.
Also, I should mention that my Register() function only accepts the most basic function signature, as in my example. Your challenge input would not be accepted b/c I don't allow nested parentheses. The single allowed signature is:
output = functionName(arg1,...)
So in answer to your question, yes I've been trying to be careful & aware of the potential complexities & downsides here. I'm deliberately trying to make this little subsystem as simple as possible.
All this said, do you have any specific suggestion for accomplishing my goal..?
Thanks,
Brad
Meta-programming has the crude side-effect, that you start with only some basic stuff. But if this works, the program will grow as each useful program.
Anyhow, you asked for specific help and if the complexity explodes in the future, you will remember my philosophic debate. What about demanding for a manual definition in the constructor?
data1 = functionA(1,2,3, 'Commandline: functionA(1,2,3)');
It is much nicer, when the source of the data is obtained automatically. But adding this string manually limits the danger of obscure side-effects and in consequence the complexity. So you can decide between boring typing or tedious debugging.
Hi Jan,
OK, I acknowledge your warning ;)
However, I can't accept your specific "solution" to my question. As I understand it, you are simply suggesting that I manually duplicate the command-line function call as a literal string (minus the duplication) & pass this as an additional function argument.
On a philosophical basis, I don't think this is a good idea. It's certainly prone to typos & therefore not foolproof to "obscure side-effects" in that respect. Furthermore, it places additional cognitive burden on the user to perform another level of careful error-checking.
As I'm sure you're aware, one of the benefits of machine processing is to *reduce* the chance for human error. IMHO, your proposed solution actually increases this harmful possibility.
In some contexts, such as when running software prompts the user to enter & re-enter a new password, it makes sense to ask the user for duplicate entry of the same info. But given that your solution does not provide for the duplicated content to be cross-verified, I don't think this can qualify as a legitimate request of the user. Can you think of any software which asks the user to enter the same information twice, but does not validate the entries against each other..? I can't think of any.
Brad
p.s. thank you anyway for your time & consideration. I appreciate you giving my problem some thought :)
@Brad: "Can you think of any software which asks the user to enter the same information twice, but does not validate the entries against each other?" Yes, Windows UAC. And I agree that it is a good idea not to compete with it.
And I agree, that my answer does *not* solve your problem.

Sign in to comment.

I think you always want the last function on the stack:
callerFileName = stDebug(end).file;
callerLineNumber = stDebug(end).line;

2 Comments

Daniel,
Thanks for your response, but did you try your suggested code..? Here's what I get when I execute your modified function B():
>> B(10)
Complete text of calling code is : 'stDebug = dbstack;'
Just to clarify, here's the call-stack output we wish to generate when B() is called from the command-line:
>>B(10)
Complete text of calling code is : 'B(10)'

Sign in to comment.

Categories

Find more on Function Creation in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!