简体   繁体   中英

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

I'm using CPPTCL to instantiate an instance of TCL interpreter in my C++ code. I can then execute scripts by use of "pTclInterpreter->eval(script);" Works as needed. I also define my own TCL functions as C++ functions and make them available for use in the TCL script by "pTclInterpreter->def("getValueCmd", getValueCmd);". Works fine, too.

Now, for messaging (info/error) inside my C++ implemented TCL functions I need to know from what script line the function was called. I found the article: Getting the line number of executing code in TCL and when I execute the TCL-Script in a standalone TCL shell I receive as expected: Global 28 proc A 23 proc B 18 proc C 13 proc D 9

But when I execute the very same script from within my C++ instantiated TCL interpreter I receive instead: Global 28 proc A 3 proc B 3 proc C 3 proc D 3

The Global call is correct at line 28. But for the function calls it is always "3", which seems to refer to being the third line in the called function - not the line in the complete script.

This leaves me with two Questions: 1.) How can I detect what TCL script line called my C++ function? 2.) If the behavior of TCL script is different in this point - where else would I have to expect differences?

In the folling my C++ code.

#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
}

This also uses SystemC which I believe to be of no further importants here. As you see, I create a TCL interpreter instance and have the pointer pTclInterpreter reference it. I then use "pTclInterpreter->eval(script);" to execute the script from the reference. But results are different from doing this in a pure shell.

Are your procedures created by source ing a file? (Well, using Tcl_EvalFile and its relations are OK too.) If not, they won't have global line numbers as there won't be a suitable context available at that point. The way that actually works is really tricky under the covers, so using source (or the C API that it calls) is by far the simplest method of making things work.

Change the above line

pTclInterpreter->eval(script);

to

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

UPDATE: I finished my demo as needed. I'm now able to create a TCL-interpreter and define C++ functions to be callable from TCL scripts. These TCL-called-C++-functions are able to now access the TCL-interpreter instance to figure out what line of script from what named file did call them. The code:

#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
}

The result after execution:

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.

Used TCL scripts:

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

Next

#!/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]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM