**Introduction** The following questions tend to be asked regularly on the comp.lang.tcl newsgroup: * ''I have a large [C]/[C++] program- how do I make it scriptable with [Tcl]?'' * ''I have a large C/C++ program- how do I add a [Tk] [GUI] to it?'' At a high level, there are three different solutions to consider. 1. Embed Tcl calls in my C/C++ code. 2. Wrap my C/C++ code to make it callable from Tcl.<
>Replace the C/C++ main() with a tclsh/wish main program 3. Something else ---- **Embed Tcl Calls in C/C++ code** While the text "[Why adding Tcl calls to a C/C++ application is a bad idea]" is an interesting dialog concerning the appropriate approach to performing this task, where can one find specific coding examples and documentation regarding what one needs to do, in a C program, if one needs to create an interpreter and then execute tcl commands making use of that interpreter. Well, one example comes ''out of the box'' with both Tcl and Tk. Take a look at the tcl/unix/tclAppInit.c (or tcl/win/tclAppInit.c) files in the tcl source distribution (which is the mainline C module for the tclsh command), and in the Tk source distribution, tk/unix/tkAppInit.c or tk/win/winMain.c (I don't understand why the name difference here...). For MacOS, see tk/mac/tkMacAppInit.c and tk/macosx/tkMacOSXAppInit.c (again, I don't understand the reasoning for file name changes). These provide at least a basic skeleton for initializing an interpreter. Unfortunately, they leave you in an interpretive mode which most people don't want to happen. So another example is needed. I do know there are two kinds of Tcl actions one can invoke from C/C++ Since Tcl is ''just'' a C library, some Tcl actions can be invoked by calling the appropriate Tcl function call. However, some things in Tcl must be done by invoking [Tcl_Eval], after appropriately setting up a Tcl interpreter. Can someone provide some sample C or C++ code that shows setting up the interpreter, then perhaps invoking things each way? A dream scenario would do this for Tcl/Tk since that is a superset of the same kind of request for Tcl... ---- **First Example of invoking Tcl** [AM] Here is some (massaged) code that I use in one application. The idea is: * Set up a basic interpreter * Register the specific commands * Make sure that the interpreter is available when a scripted task is called It ''does not'' use the Tcl script library, only the basic built-in commands are available, but this is a very simple set-up after all. The function InitScript() is used to set up a script library private to the application and to get a functioning Tcl interpreter. The function EvalScriptCommand() is used elsewhere and simply wraps the details so that I do not need to use Tcl routines everywhere. ======c #include /* * Static global data */ static Tcl_Interp * tcl_interp ; /* Script interpreter */ int InitScript( void /* Nothing */ ) /* Return okay or not */ { char * pstr ; FILE * infile ; int retval ; int rc ; size_t filesize ; tcl_interp = Tcl_CreateInterp() ; if ( tcl_interp == NULL ) { fprintf( stderr, "Could not create interpreter!\n" ) ; return 1 ; } /* Register the commands specific to my application Tcl_CreateObjCommand( tcl_interp, "session", GppSessionCmd, (ClientData) NULL, GppDummyDestroy ) ; /* Read the configuration file with specific script code (in reality, I use a function that searches for the file and opens it! Hence I need to do more work ... If you have the file name, then use Tcl_EvalFile()!) */ infile = fopen( "scripts.conf", "r" ) ; if ( infile == NULL ) { return 1 ; } /* Now, read the whole file ... */ fseek( infile, 0L, SEEK_END ) ; filesize = ftell( infile ) ; pstr = (char * ) malloc( (filesize+1) * sizeof(char) ) ; if ( pstr == NULL ) { return 1; } fseek( infile, 0L, SEEK_SET ) ; fread( pstr, filesize, 1, infile ) ; pstr[filesize] = '\0' ; rc = Tcl_Eval( tcl_interp, pstr ) ; if ( rc != TCL_OK ) { fprintf( stderr, "Error loading script library\n" ) ; return 1 ; } free( pstr ) ; return 0; } /* Function for encapsulating the details */ EvalScriptCommand( char * command ) { if ( Tcl_Eval( tcl_interp, command ) == TCL_OK ) { return 0 ; } else { return 1 ; } } //compile with g++ and run using ./exec ====== ---- **Second Example of Invoking Tcl from a C Application** David Gravereaux writes, on comp.lang.tcl, this code fragment for invoking Tcl from a C program: ======c Tcl_Interp *interp; int Init (char *argv0) { Tcl_FindExecutable(argv0); interp = Tcl_CreateInterp(); if (Tcl_Init(interp) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } void TearDown () {Tcl_Finalize();} int EvalFile (char *fileName) { return Tcl_EvalFile(interp, fileName); } ====== If EvalFile() returns TCL_ERROR, get the error with `Tcl_GetStringResult()` . ---- **Wrap C/C++ code to make it callable from Tcl** [Lectus] I've beeing experimenting with "GUIfying" C/C++ programs using Tcl/Tk. Here is how to do it (I tried to make the example simple so it's easy to understand): C++ code: ====== #include #include #include using namespace std; // The procedure to provide the add function void add(string x, string y) { int ix=0; int iy=0; // We use stringstream to convert from string to int. stringstream ssx(x); stringstream ssy(y); ssx >> ix; ssy >> iy; // Output the result to stdout. cout << ix+iy << endl; } // We'll access C or C++ code by passing parameters to the command line program int main(int argc, char **argv) { // Test if we have all args if (argc < 4) { cout << "Error" << endl; return 1; } // Get the operator and check to see if it's + string opt = argv[1]; if (opt == "+") { // Yes, it is, so call add function. add(argv[2],argv[3]); } return 0; } ====== compiled with $ g++ test.cpp -o test And here is the Tcl/TK code with the GUI part: ====== package require Tk ttk::label .x -text "X: " ttk::entry .ex -textvar x ttk::label .y -text "Y: " ttk::entry .ey -textvar y ttk::button .badd -text "Add" ttk::label .res -text "Result: " grid .x .ex -padx 5 -pady 2.5 grid .y .ey .badd -padx 5 -pady 2.5 grid .res -padx 5 -pady 2.5 focus .ex .badd config -command { if {[catch {open [list "|./test" + $x $y] "r"} fd]} { puts "Couldn't open pipe." exit } set z [gets $fd] close $fd .res config -text "Result: $z" } ====== Note: Tested on Linux. On windows you probably need to replace "./test" for "test". ---- **See also** * [Writing Tcl-Based Applications in C] <> Tutorial