获取在C++中实例化的TCL解释器中执行代码的行号

Getting the line number of executing code in TCL-Interpreter instantiated in C++

本文关键字:执行 代码 解释器 TCL 实例化 获取 C++      更新时间:2023-10-16

我在C++代码中使用CPPTCL实例化TCL解释器的一个实例。然后,我可以根据需要使用"pTclInterpreter->eval(script);"来执行脚本。我还将自己的TCL函数定义为C++函数,并通过"pTclInterpreter->def("getValueCmd",getValueCmd);"使其可在TCL脚本中使用。效果也不错。

现在,对于C++实现的TCL函数中的消息传递(info/error),我需要从调用该函数的脚本行中知道。我找到了这篇文章:获取TCL中执行代码的行号,当我在独立的TCL shell中执行TCL脚本时,我收到了预期的结果:全球的28程序A23程序B18程序C13程序D9

但是,当我在C++实例化的TCL解释器中执行相同的脚本时,我收到的却是:全球的28程序A3.程序B3.程序C3.程序D3

第28行的全局调用正确。但对于函数调用,它总是"3",这似乎指的是被调用函数中的第三行,而不是完整脚本中的行。

这给我留下了两个问题:1.)如何检测TCL脚本行称我的C++函数?2.)如果TCL脚本的行为在这一点上有所不同,我还能在哪里期待差异?

在我愚蠢的C++代码中。

#include <systemc.h>
#include "cpptcl.h"
sc_event  go;
Tcl::interpreter *pTclInterpreter;
int MOD(std::string module, std::string cmd, int param1, int param2){
    /* 
        Function to simulate the execution of an IFS command.
    */
    /*
        Here we would have the code that creates the proper cmdObj
    */
    std::cout << sc_time_stamp() << ": creatingCmdObj(";
    std::cout << module;
    std::cout << ", " << cmd;
    std::cout << ", " << param1;
    std::cout << ", " << param2;
    std::cout << ")"  << std::endl;
//  pTclInterpreter->eval("puts [ dict get [info frame [info frame -1] ]  line ]");
    return 0;
}
int getValueCmd(){
    /*
        Function to simulate the execution of a getValue
    */
    std::cout << sc_time_stamp() << ": executing getValueCmd." << std::endl;
    std::cout << sc_time_stamp() << ": TCL waiting for all TBMs to catch up." << std::endl;
    wait(go);
    std::cout << sc_time_stamp() << ": TBMs in sync. TCL interpreter to continue." << std::endl;
    return 0;
}
int read_trace(const int& v, void *) {
  cout << "read trace triggered: wait(10, SC_NS)" << endl;
  wait(10, SC_NS);
  return v;
}
SC_MODULE (wait_example) {
  void tclInterpreter() {
    std::cout << sc_time_stamp() << ": Starting TCL interpreter!" << std::endl;
    // new TCL
    pTclInterpreter = new(Tcl::interpreter);
    // register the C++ function to be called from TCL
    pTclInterpreter->def("MOD", MOD);
    pTclInterpreter->def("getValueCmd", getValueCmd);
    // register read trace for variable
    pTclInterpreter->def_read_trace("tracedVar", "read", read_trace);
    // load the script
    // ifstream script("helloworld.tcl");
    ifstream script("test.tcl");
    // run the script with the given arguments
    pTclInterpreter->eval(script);
    cout << sc_time_stamp() << ": Terminating Simulation." << std::endl;
    sc_stop(); // sc_stop triggers end of simulation
  }
  void ifscontroller() {
    wait(100, SC_NS);
    go.notify();
  }
  SC_CTOR(wait_example) {
    SC_THREAD(tclInterpreter);
    SC_THREAD(ifscontroller);
  }
}; 
int sc_main (int argc, char* argv[]) {
  wait_example object("wait");
  sc_start(0, SC_NS); // First time called will init schedular
  sc_start();  // Run the simulation till sc_stop is encountered
  return 0;// Terminate simulation
}

这也使用了SystemC,我认为它在这里不再重要。如您所见,我创建了一个TCL解释器实例,并让指针pTclInterpreter引用它。然后我使用"pTclInterpreter->eval(script);"从引用中执行脚本。但结果不同于在纯壳中这样做。

您的过程是通过source创建文件的吗?(好吧,使用Tcl_EvalFile及其关系也可以。)如果没有,它们就不会有全局行号,因为此时没有合适的上下文可用。实际工作的方式实际上很棘手,因此使用source(或其调用的C API)是迄今为止最简单的工作方法。

更改以上行

pTclInterpreter->eval(script);

pTclInterpreter->eval("source test.tcl");

更新:我根据需要完成了演示。我现在可以创建一个TCL解释器,并定义可从TCL脚本调用的C++函数。这些TCL-调用的C++函数现在可以访问TCL解释器实例,以确定哪个命名文件中的哪行脚本调用了它们。代码:

#include <systemc.h>
#include "cpptcl.h"
sc_event  go;
Tcl::interpreter *pTclInterpreter;
int MOD(std::string module, std::string cmd, int param1, int param2){
    /*
        Here we would have the code that creates the proper cmdObj and adds it to
        the IFS controller's list of cmdObj. From there the TBMs will receive it
        during their executeNextCommand loop.
    */
    pTclInterpreter->eval("set IFS_scriptLine [dict get [info frame [expr [info frame] -1] ] line]");
    pTclInterpreter->eval("set IFS_scriptName [dict get [info frame [expr [info frame] -1] ] file]");
    std::cout << sc_time_stamp() << "; " << pTclInterpreter->read_variable<std::string>("IFS_scriptName") << "(";
    std::cout << pTclInterpreter->read_variable<int>("IFS_scriptLine") << ")";
    std::cout << ": creatingCmdObj(";
    std::cout << module;
    std::cout << ", " << cmd;
    std::cout << ", " << param1;
    std::cout << ", " << param2;
    std::cout << ")"  << std::endl;
    return 0;
}
int getValueCmd(){
    pTclInterpreter->eval("set IFS_scriptLine [dict get [info frame [expr [info frame] -1] ] line]");
    pTclInterpreter->eval("set IFS_scriptName [dict get [info frame [expr [info frame] -1] ] file]");
    std::cout << sc_time_stamp() << "; " << pTclInterpreter->read_variable<std::string>("IFS_scriptName") << "(";
    std::cout << pTclInterpreter->read_variable<int>("IFS_scriptLine") << ")";
    std::cout << ": executing getValueCmd." << std::endl;
    std::cout << sc_time_stamp() << ": TCL waiting for all TBMs to catch up." << std::endl;
    wait(go);
    std::cout << sc_time_stamp() << ": TBMs in sync. TCL interpreter to continue." << std::endl;
    return 0;
}
int read_trace(const int& v, void *) {
  cout << "read trace triggered: wait(10, SC_NS)" << endl;
  wait(10, SC_NS);
  return v;
}
SC_MODULE (wait_example) {
  void tclInterpreter() {
    std::cout << sc_time_stamp() << ": Starting TCL interpreter!" << std::endl;
    // new TCL
    pTclInterpreter = new(Tcl::interpreter);

    // register the C++ function to be called from TCL
    pTclInterpreter->def("MOD", MOD);
    pTclInterpreter->def("getValueCmd", getValueCmd);
    // register read trace for variable
    pTclInterpreter->def_read_trace("tracedVar", "read", read_trace);
    // run the script with the given arguments
    pTclInterpreter->eval("source helloworld.tcl");
    cout << sc_time_stamp() << ": Terminating Simulation." << std::endl;
    sc_stop(); // sc_stop triggers end of simulation
  }
  void ifscontroller() {
    wait(100, SC_NS);
    go.notify();
  }
  SC_CTOR(wait_example) {
    SC_THREAD(tclInterpreter);
    SC_THREAD(ifscontroller);
  }
}; 
int sc_main (int argc, char* argv[]) {
  wait_example object("wait");
  sc_start(0, SC_NS); // First time called will init schedular
  sc_start();  // Run the simulation till sc_stop is encountered
  return 0;// Terminate simulation
}

执行后的结果:

0 s: Starting TCL interpreter!
             SystemC 2.3.0-ASI --- May 16 2014 17:16:28
        Copyright (c) 1996-2012 by all Contributors,
        ALL RIGHTS RESERVED
"Executing script and creating cmdObj."
0 s; myCppTcl/helloworld.tcl(5): creatingCmdObj(tbm1, userDefined1, 7, 12)
0 s; myCppTcl/helloworld.tcl(6): creatingCmdObj(tbm2, read, 9, 1)
0 s; myCppTcl/helloworld.tcl(8): creatingCmdObj(tbm2, write, 6, 6)
0 s; myCppTcl/helloworld.tcl(9): creatingCmdObj(tbm1, userDefined4, 2, 4)
0 s; myCppTcl/helloworld.tcl(14): executing getValueCmd.
0 s: TCL waiting for all TBMs to catch up.
100 ns: TBMs in sync. TCL interpreter to continue.
100 ns; myCppTcl/helloworld.tcl(19): creatingCmdObj(tbm1, read, 3, 0)
100 ns; myCppTcl/helloworld.tcl(19): creatingCmdObj(tbm1, read, 4, 1)
100 ns; myCppTcl/helloworld.tcl(19): creatingCmdObj(tbm1, read, 5, 2)
100 ns; myCppTcl/helloworld.tcl(19): creatingCmdObj(tbm1, read, 6, 3)
100 ns; myCppTcl/helloworld.tcl(19): creatingCmdObj(tbm1, read, 7, 4)
100 ns; myCppTcl/helloworld.tcl(19): creatingCmdObj(tbm1, read, 8, 5)
read trace triggered: wait(10, SC_NS)
10
read trace triggered: wait(10, SC_NS)
10
read trace triggered: wait(10, SC_NS)
10
read trace triggered: wait(10, SC_NS)
10
read trace triggered: wait(10, SC_NS)
10
read trace triggered: wait(10, SC_NS)
10
160 ns; myCppTcl/testSource.tcl(7): creatingCmdObj(tbm1, userDefined1, 7, 12)
160 ns; myCppTcl/testSource.tcl(4): creatingCmdObj(tbm1, userDefined1, 7, 12)
160 ns: Terminating Simulation.
Info: /OSCI/SystemC: Simulation stopped by user.

使用的TCL脚本:

puts {"Executing script and creating cmdObj."}
# Regular IFS commands  to be put here.
# Each command creates individual cmdObj
MOD tbm1 userDefined1 [expr 3+4] [expr 4*3]
MOD tbm2 read [expr 3*3] 1
set myVar 6
MOD tbm2 write [expr 12-6] $myVar
MOD tbm1 userDefined4 [expr 20/10] 4
# Need to receive value from IFS. Cannot
# continue executing script.
# getValueCmd makes us wait for TBMs
getValueCmd
# Regular IFS commands  to be put here.
# Each command creates individual cmdObj
for { set i 0 } { $i <= 5 } { incr i } {
  MOD tbm1 read [expr 3+$i] $i
}
set tracedVar 10
# Assuming that tracedVar is an IFS-Signal type var, it is required
# to synchronize the IFS and it's testbench modules. For demonstration
# the read call back for the following command will wait 10 ns.
for { set i 0 } { $i <= 5 } { incr i } {
  puts $tracedVar
}
# Let us see what file name and line numbers are communicated when we use source
source testSource.tcl

下一个

#!/usr/bin/tclsh
proc testProc {} {
        # Execute an IFS command in a procedure to see what file and line info is communicated
        MOD tbm1 userDefined1 [expr 3+4] [expr 4*3]
}
MOD tbm1 userDefined1 [expr 3+4] [expr 4*3]