jpo38
jpo38

Reputation: 21514

C++/Tcl Can I retrieve Tcl file line number when a function recorded by Tcl_CreateCommand is invoked

I run a Tcl script from a C++ program using Tcl_EvalFile. I defined some custom commands (using Tcl_CreateCommand), so when they are found in the file a callback is called and then I can run some C++ code (TclInvokeStringCommand invokes the callback given to Tcl_CreateCommand, the callback type is int (*executeCmd)( ClientData data, Tcl_Interp *interp, int argc, const char *argv[] )).

I want to know the script file name and line number within the callback function being invoked.

I could get the script file name using ((Interp*) interp)->scriptFile.

However, I could not get the script file line number. Is there a way to retrieve this (or compute it in any way)?

Upvotes: 1

Views: 350

Answers (1)

Donal Fellows
Donal Fellows

Reputation: 137587

The information is only exposed at the Tcl level via the info frame command. (The internal C API for it is sufficiently horrible that it's never been made public.) This means you need to do something like this with Tcl_Eval():

// These can be cached safely per thread; reference management is a thing for another question
Tcl_Obj *scriptNameHandle = Tcl_NewStringObj("file", -1);
Tcl_Obj *lineNumberHandle = Tcl_NewStringObj("line", -1);

// How to actually get the information; I'm omitting error handling
Tcl_Eval(interp, "info frame -1");
Tcl_Obj *frameDict = Tcl_GetObjResult(interp);
Tcl_Obj *scriptNameObj = nullptr, *lineNumberObj = nullptr;
Tcl_DictObjGet(nullptr, frameDict, scriptNameHandle, &scriptNameObj);
Tcl_DictObjGet(nullptr, frameDict, lineNumberHandle, &lineNumberObj);

// Now we have to unbox the information
if (scriptNameObj != nullptr) {
    const char *filename = Tcl_GetString(scriptNameObj);
    // Do something with this info; COPY IT if you want to keep past the result reset
}
if (lineNumberObj != nullptr) {
    int lineNumber = -1;
    Tcl_GetIntFromObj(nullptr, lineNumberObj, &lineNumber);
    // Do something with this info
}

// Release the result memory, i.e., the dictionary and its contents
Tcl_ResetResult(interp);

Note that neither the file nor the line keys are guaranteed to be present; the line key is usually there, but the file key is only there when running code that can be tracked back to a file, i.e., back to Tcl_EvalFile() and related.

Upvotes: 1

Related Questions