Tcl_Obj* objPtr[2]; objPtr[0] = Tcl_NewStringObj( "info", -1 ); objPtr[1] = Tcl_NewStringObj( "commands", -1 ); // I know this function is internal to Tcl, but it seems no other C // functions to get all command names out of Tcl. Tcl_InfoObjCmd( 0, d_interp, 2, objPtr ); return getResult().split( " ", QString::SkipEmptyParts );
2009-06-18
Get a list of all Tcl commands by Tcl_InfoObjCmd
2009-06-11
Auto-execute shell built-ins or executables in Tcl interactive mode
2009-05-06
Implement your own shell mode based on Tcl_Main()
In case your clients told you that they will be very happy to see a shell mode of your application implemented because they see this mode as a productivity add (why? who knows? customers always rule!)
Your application already has Tcl interpreter embedded. Why not just take advantage of the code written by Tcl developers? Using it will save us a lot of efforts. After all, you're hired to solve problems, not to reinvent the wheels. But, how?
Tcl has defined a function Tcl_Main() which is the entry function of tclsh. Now, take a look at the generic/tclMain.c in Tcl source. It is called as,
Tcl_Main(argc, argv, AppInit /* init function */);
and this ~AppInit~ must be declared as
int AppInit(Tcl_Interp* interp);
In my implementation, this AppInit function is responsible for
- Set Tcl library paths pointed by tcl variable tcl_library.
- Hack tcl command /history/ so that every time a Tcl command is executed at the shell, it is logged at log file (See definition of Tcl_RecordAndEvalObj() in generic/tclHistory.c). The hack should look something like,
if {[auto_load ::history]} {#this is why library_path must be specified rename ::tcl::HistAdd ::Init::OldHistAdd } proc ::tcl::HistAdd {command {exec {}}} { # ... # my log command if {[catch {interp invokehidden {} _log_command_ --cmd [string trim $command]}] != 0} { puts "# ERROR: Command '[string trim $command]' is not logged" return {} } # }You should then provide a hidden command _log_command_ for logging. - Register exit handler by Tcl_CreateExitHandler() for cleanup.
- Source my initialization scripts.
- Register my commands in Tcl interpreter.
- Change prompt string.
set tcl_prompt1 {puts -nonewline stdout {my_shell> }};
2009-05-01
Wrap Tcl_ObjCommand in an object-oriented way
People know that Tcl is widely used in the world. It uses C-like syntax and provides Tcl/C binding. For people who know C and want their applications to support script, Tcl is a good solution.
Tcl is written in C. If a Tcl interpreter is required to be embedded in the application and you want to write Tcl command in C/C++, we usually need to do something that makes Tcl a bit object-oriented. How to create an object-oriented Tcl language is not my intention. What I am trying to do is to wrap Tcl in a way that writing of Tcl commands can be more object-oriented (maybe you can see it as a pattern). And if you know any better way, please let me know.
If we are required to create a command called run_me in Tcl interpreter and it is kinda expensive to write this command in tcl script, we can write it in C/C++. I am sure we all agree to this. The internal work is that an associated C function gets called when run_me is executed in tclsh.
First, a Tcl_CreateObjCommand can be used to create this Tcl to C binding and it needs parameters as follows:
Tcl_CreateObjCommand(interp, /* Tcl_Interp* */
"run_me", /* const char*: Tcl command name */
&my_run_me_c_func, /* C function to be called */
0, 0 );
In the simplest form, you can just add my_run_me_c_func at global namespace. Of course, it's not a good enough solution for C++ developer because you may add too many functions in global namespace. It shouldn't surprise you that I use a class hierarchy to manage these C bindings. And it shouldn't surprise you again that I apply Command Pattern in this scenario. The base class of the hierarchy may look like,
class TclCommand {
public:
virtual ~TclCommand() {}
int execute( ClientData clientData,
Tcl_Interp* interp,
int objc,
Tcl_Obj* const objv[] ) {
// print help if having -h option
// syntax checking by is_valid()
// everything is fine, call do_execute()
return TCL_OK;
}
protected:
TclCommand();
virtual int do_execute( /* necessary arguments */ ) = 0;
virtual bool is_valid( /* arguments from command line */ ) = 0;
virtual void help( /* necessary arguments */ ) = 0;
};
class RunMeTclCommand : public TclCommand {
private:
// Override pure virtual functions in base class.
};
For different Tcl commands, different TclCommand class should be added in this class hierarchy. The problem is how to let Tcl know run_me and RunMeTclCommand are connected?
First, every Tcl command is linked to a local static function TclCommandLinker and it dispatches call to appropriate TclCommand object. Its content may look like
static int
TclCommandLinker( ClientData clientData,
Tcl_Interp* interp,
int objc,
Tcl_Obj* const objv[] )
{
// ...
int length = 0;
TclCommand* cmd = TclCommandMap::instance()->getMapping(
Tcl_GetStringFromObj( objv[0], &length ) );
if ( cmd ) {
return cmd->execute( clientData, interp, objc, objv );
}
return TCL_ERROR;
}
You can see from above that a singleton object TclCommandMap used here to get proper TclCommand mapping. This object can be implemented by std::map as,
class TclCommandMap {
public:
static TclCommandMap* instance();
void addMapping(const std::string& name, TclCommand* cmd);
Command* findMapping(const std::string& name);
// ...
};
Second, Tcl knows this TclCommandLinker through,
Tcl_CreateObjCommand( interp,
"run_me",
TclCommandLinker,
0, 0 );
Finally, when a Tcl command is registered into Tcl interpreter,
// ...
TclCommandMap::instance()->addMapping("run_me",
new RunMeTclCommand( /* parameters */ );
// ...
That's it. We're done.