''RS: The mangled language name in the title is because spelling it C++ resulted in a misparse of [New pages]... See [Wikit problems]'' ---- ''BGE'' (9 January 2001) — Fixed problems with formatting so people can copy-paste code more-or-less directly. ---- ''KBK'' (8 January 2001) — If you're integrating Tcl and C++, then [Cplusplus streams and Tcl channels] may also be of interest. ---- [willdye] (2004-12-27) See also: [cpptcl], [C++], [C++/Tcl], and many others. ---- Date: 01/06/2001 (on comp.lang.tcl) Author: Benoit Goudreault-Emond Just thought I'd share the following small bit of code I've had lying around for quite some time. It's an adaptation of [Brent Welch]'s “[TclFastInvoke]” routine from his book into C++ land. In a nutshell, it allows direct invocation of Tcl and Tk library routines without going through the interpreter. This has two advantages: 1. It's faster (direct function call instead of indirect through interpreter) 2. It totally eliminates quoting issues. Sometimes, you just want to pass a plain string to the command without needing to add backslashes everywhere; this is where this can be handy. It's part of the Voodoo UML Class Diagram Editor ( http://voodoo.sourceforge.net ) and may be useful to people who want to use Tcl/Tk the same way it's done in there. It requires an ANSI C++ compliant compiler; if you're compiling under VC++, you should #define WIN32 before compilation because VC++ is broken... Example of usage: ======c Tcl_Interp interp; // ... TclCommander cmd(interp); // build command list cmd("font") << "metrics" << "Times 12" << "-fixed"; if(cmd.invoke() != TCL_OK) return TCL_ERROR; if(*Tcl_GetStringResult(interp) == '1') // ... ====== The above code snippet is the equivalent of calling ====== [font metrics {Times 12} -fixed] ====== in Tcl and testing the return value. Not very useful, but you can see how useful it could be in general. The `<<` operator “pushes” arguments in the argument list; you can pass strings, doubles and ints at the moment. It should be relatively easy to extend to other types as well. The `invoke()` member function calls the command with the given arguments. This was roughly twice as fast as going through `[Tcl_Eval]()` in my informal tests. Hope this is of some use! Feel free to improve on it — the code below can be considered Public Domain. I don't expect to improve it further at this time, as it does enough for what I use it for, but it could no doubt become a more generic Tcl/C++ binding. ---- '''tclbuf.h:''' ======c /* -*- C++ -*- */ #ifndef TCLBUF_H #define TCLBUF_H /* * tclbuf.h -- Houses a fast TCL invocation engine. * * $Id: 1133,v 1.7 2006-01-19 07:00:09 jcw Exp $ */ /** This class allows fast invokes -- without having to convert all your junk to const char*. */ #include using std::string; #include #ifdef WIN32 #define defaultCapacity 10 #endif // WIN32 class TclCommander { #if !defined(WIN32) static const size_t defaultCapacity = 10; #endif // !defined(WIN32) public: TclCommander(Tcl_Interp* interp) : argc(0), objv(objv1), objvSize(defaultCapacity), isCmdCached(false), interp_(interp) { } ~TclCommander() { reset(); if(objv != objv1) delete[] objv; } /// sets command and checks if it exists at the same time. Returns false /// if the command is nonexistent. bool command(const char* cmd); bool command(const string& cmd); /// append different arguments. void push(const char* arg) { // the const_cast is ugly, but it's required only because of // the stoopid way Tcl has its header files done. Normally, // the string is not really modified. push(Tcl_NewStringObj(const_cast(arg), strlen(arg))); } void push(const string& arg) { // const_cast required for reasons given above. push(Tcl_NewStringObj(const_cast(arg.data()), arg.length())); } void push(int arg) { push(Tcl_NewIntObj(arg)); } void push(long arg) { push(Tcl_NewLongObj(arg)); } void push(double arg) { push(Tcl_NewDoubleObj(arg)); } void push() { push(Tcl_NewObj()); } void push(Tcl_Obj* arg); /// invokes the command. Returns the result from the command. int invoke(); /// just a convenience binding to use the commander as a function /// object. int operator() () { return invoke(); } /// resets the current arguments for a new run. This also nulls the /// command out, so you'll have to call command() again. void reset(); //**************** OPERATORS **************** /// append different types of objects /// I'd use templates here, but it would slow compilation TclCommander& operator<< (const char* arg) { push(arg); return *this; } TclCommander& operator<< (const string& arg) { push(arg); return *this; } TclCommander& operator<< (int arg) { push(arg); return *this; } TclCommander& operator<< (long arg) { push(arg); return *this; } TclCommander& operator<< (double arg) { push(arg); return *this; } /// a shortcut for command TclCommander& operator() (const char* cmd) { ASSERT(command(cmd)); return *this; } TclCommander& operator() (const string& cmd) { ASSERT(command(cmd)); return *this; } private: /// default vector, to avoid ugly calls to new[] in most cases Tcl_Obj* objv1[defaultCapacity]; int argc; Tcl_Obj** objv; int objvSize; bool isCmdCached; Tcl_CmdInfo cmdInfo; Tcl_Interp* interp_; bool command(const char* cmd, size_t length); }; #endif ====== ---- '''tclbuf.cc:''' ======c #include #include #include #include #include #include #include "tclbuf.h" using namespace std; bool TclCommander::command(const char* cmd, size_t length) { char* temp = new char[length+1]; copy(cmd, cmd+length, temp); temp[length] = 0; bool retval = command(temp); delete[] temp; return retval; } bool TclCommander::command(const char* cmd) { // make sure we kill previous commands if necessary if(isCmdCached) reset(); // get info; we must allocate a temporary for that if(!Tcl_GetCommandInfo(interp_, const_cast(cmd), &cmdInfo)) return false; objv[argc++] = Tcl_NewStringObj(const_cast(cmd), strlen(cmd)); isCmdCached = true; return true; } bool TclCommander::command(const string& cmd) { return command(cmd.c_str()); } void TclCommander::push(Tcl_Obj* arg) { if(argc >= objvSize) { // we just ran out of space in the array. Alloc 2*objvSize. // this makes array grow exponentially; it's the usual // tradeoff also found in most implementations of std::vector<> Tcl_Obj** temp = new Tcl_Obj* [objvSize*2]; copy(objv, objv+objvSize, temp); objvSize *= 2; if(objv != objv1) delete[] objv; objv = temp; } Tcl_IncrRefCount(arg); objv[argc++] = arg; } int TclCommander::invoke() { // NOTE: you should *not* use cout in normal Tcl programs-- // output through channels instead. Following code only // illustrates how to put a trace on the system. #ifdef DEBUG cout << "=> " << Tcl_GetStringFromObj(objv[0],NULL) << endl; for(int i = 1;i < argc;++i) cout << "--> " << Tcl_GetStringFromObj(objv[i],NULL) << endl; #endif return (*cmdInfo.objProc)(cmdInfo.objClientData, interp_, argc, objv); } void TclCommander::reset() { for(int i = 0;i < argc;++i) Tcl_DecrRefCount(objv[i]); argc = 0; isCmdCached = false; } ====== Benoit Goudreault-Emond -- WWW: http://www.crosswinds.net/~bge CoFounder, KMS Group ; Programmer, Silanis Technology (http://www.silanis.com) Note: the "From:" address is not correct to protect myself against spam. My actual e-mail address is: `bge AT crosswinds DOT net` ---- [AMG]: The above code is LGPL, as is Voodoo. Wait, on second thought, maybe it isn't, since it's derived from Brent Welch's book. ---- [RFox]: May 31, 2012 - A C++ class library that encapsulates a pretty good chunk of Tcl's API has been part of a few products I've written for NSCL for quite some years. A summary of the library is: http://docs.nscl.msu.edu/daq/ringbuffer/c2407.html Detailed reference pages are at http://docs.nscl.msu.edu/daq/ringbuffer/index.html - search for CTCL and you'll find the manpages for the various classes that make up the beast. I can make this more generally available (GPL sorry but that's what the MSU board of trustees and I agreed on) if there's a desirement. <> Development