By Ankur Kumar Sharma
C and C++ are compiled languages: The source code is translated to the machine code of the hardware platform in a complete binary file. To run any C or C++ program, you first need to write the source code, then compile and link the code to create an executable. Every time you make even a small change, you have to repeat this code/modify/compile/link/run cycle. For small programs, the compile/link step is trivial, but for even a medium-size program, this step is very time consuming and boring. A large C or C++ program can take several hours to compile and link. This process is especially annoying if you are a beginner who is prone to small errors, or even if you are a seasoned veteran who is interested in rapid prototyping.
In contrast, scripting languages like Python, Ruby, and Perl don't require a compile/link step. Scripting languages also provide the opportunity to experiment by typing program code into the interactive shell to see the results. In many cases, however, C and C++ are still better long-term solutions for compatibility and performance reasons.
As you might guess, having some way to run your C/C++ code directly - without the compile/link step - in an interactive shell can provide significant benefits for testing and prototyping. The open source community has responded to this call by providing several options for running C/C++ code in an interpreted environment.
In the Lab |
I used Puppy Linux 5.0 and Ubuntu 9.10 64-bit desktop edition to test the C and C++ code mentioned in this article. |
C and C++ interpreters can work in either interactive mode or batch mode. Interactive mode is like programming shells of various scripting languages: You type C and C++ program statements at the interpreter prompt one at a time and see the corresponding execution results. In batch mode, you run single or multiple C/C++ source files through an interpreter. Interactive mode is more suitable for experimentation with short code snippets, and batch mode is better when the source code is long or organized in multiple files.
Tcc [1], which stands for Tiny C Compiler, is available for both 32- and 64-bit target platforms. This tiny dynamo, known as a fast and efficient C compiler with a small footprint, is heading toward full ISO99 compliance and can run any C code directly without any compile/link step. Tcc comes pre-installed as the default C compiler in Damn Small Linux. It doesn't have an interactive shell, but it can run C code organized in single or multiple C source files.
To build and install Tcc, the prerequisite is GCC. To begin, download the latest Tcc source tarball from the project page, then issue the following commands in a text console:
tar zxvf tcc-version.tar.bz2 cd tcc-version
Next, issue the following commands to build and install Tcc:
./configure make sudo make install
If everything goes well, typing tcc
will bring up some help text. To test the direct C code run feature, run the code in Listing 1 by typing the command tcc -run hellotcc.c
. Tcc can also run any C code contained in a single file directly as a script if you add the line #! location_of_tcc/tcc -run
at the top of the source file and make the source file executable with the command chmod u+x filename.c
.
Listing 1: hellotcc.c |
01 #include <stdio.h> 02 03 int main(int argc, char *argv[]) 04 { 05 06 printf("\n Hello to FOSS from tcc!!!\n"); 07 return 0; 08 09 } |
Tcc can read code from the standard input instead of from a source file. Type the following in a text console to see this feature of Tcc in action:
echo 'main(){printf("\n"); system("uname -a"); printf("\n");}' | tcc -run -
To run C code organized in multiple files, the -run
option could come after one or more files in the list. For instance, you could type tcc stack.c -run teststack.c
or tcc teststack.c stack.c -run
, but Tcc throws an error if you type tcc -run teststack.c stack.c
.
Figure 1 shows the output produced by Tcc running C code directly. The files used for this example are available at the Linux Magazine website [2]. Note that Tcc is indicating some warnings even in the direct code run mode. So you can realize the convenience and saving in efforts when running both trivial as well as complicated C programs through Tcc's direct code run feature.
PicoC [3] is a very small footprint interactive C code interpreter. It offers few constructs of the C language and is mainly targeted toward small devices like embedded systems. Still, I found PicoC useful for simple to moderately difficult C code interpretation. The constructs supported by PicoC are built in as commands, so you don't have to include standard header files while working with this tiny compiler.
To build PicoC from source code, you need GCC. To build PicoC, download the latest source tarball from the project page and issue the following commands:
tar jxvf picoc-version.tar.bz2 cd picoc-version
Now type make
to build PicoC, and, optionally, make test
to run the tests that come with the package. If compilation completes with no errors, you should see a picoc
executable in the source directory. You have to copy PicoC to any of the standard locations, like /usr/local/bin
or add the absolute path of the directory to .bashrc
:
export PATH=path_of_picoc_directory:$PATH
The tests
subdirectory in the PicoC source directory contains many examples of the C language constructs supported by PicoC. To get a taste of the interactive mode functionality, type picoc -i
in a text console and enter the code in Listing 2 at the PicoC prompt. Or, to interpret the file in batch mode, type picoc testpicoc.c
. Figure 2 shows a screenshot of PicoC running in batch mode.
Listing 2: testpicoc.c |
01 char *sLP64 = " The platform is 64 bit."; 02 char *sILP32 = " The platform is 32 bit."; 03 04 int i = sizeof(int); 05 int l = sizeof(long); 06 07 if(l != i) 08 printf("%s\n", sLP64); 09 else 10 printf("%s\n", sILP32); 11 12 printf(" strlen(sLP64) : %u\n", strlen(sLP64)); 13 printf(" sizeof(sILP32) : %u\n", sizeof(sILP32)); |
EiC [4] stands for Extensible Interactive C. It is an interactive C code interpreter that can communicate with external applications. EiC provides some very useful features, like pointer safety, and some extra C programming constructs that are not found in other C code interpreters.
Download the source tarball from the project page and issue the following commands:
tar zxvf EiCsrc-version.tar.gz cd EiC-version
The following builds and installs EiC:
./config/makeconfig make install
If everything goes well, add the following EiC-specific setting in .bashrc
to finalize the configuration:
EICBASE=path_of_EiC_directory export HOMEofEiC=$EICBASE
Start EiC by typing eic
in a text console; by default, it starts in interactive mode, where you can type various C code statements and see the corresponding results instantly. (Type eic -h
to view some text-based help.)
EiC can run in a scripting mode in addition to the interactive and batch modes. Scripting mode lets you write a C program as you would a shell script; you can put various C statements in a file with or without a main()
function. To get a feeling for EiC scripting mode, run the code shown in Listing 3 by making the file executable with the command chmod u+x testscript.eic
and then typing ./testscript.eic
in a text console.
Listing 3: testscript.eic |
01 #! /usr/local/bin/eic -f 02 03 #include stdio.h 04 05 unsigned mystrlen(char *sStr) { 06 07 unsigned l; 08 09 for(l=0; *sStr; ++sStr, ++l); 10 11 return l; 12 13 } 14 15 void mystrcpy(char *sSrc, char *sDst) { 16 17 while(*sDst++ = *sSrc++); 18 19 } 20 21 int main() 22 { 23 24 char *sSrc = "This is source string."; 25 char sDst[] = "This is destination string."; 26 27 printf("\n Before mystrcpy() =>\n"); 28 printf(" sSrc : %s\n", sSrc); 29 printf(" sDst : %s\n", sDst); 30 31 if(mystrlen(sDst) > mystrlen(sSrc)) 32 mystrcpy(sSrc, sDst); 33 34 printf("\n After mystrcpy() =>\n"); 35 printf(" sSrc : %s\n", sSrc); 36 printf(" sDst : %s\n", sDst); 37 38 return 0; 39 40 } |
In Listing 3, note #include stdio.h
. With EiC you can alternatively include the headers without angle brackets.
Now I'll hack into some unique features not found with other C code interpreters. EiC can memorize all the C code statements entered on its interactive prompt. Actually, EiC creates a file called EiChist.lst
, where it stores all the commands that were interpreted without error. By default, this file is created again in the directory every time EiC is started in the interactive mode, but you can alter this behavior by launching EiC with the -n
switch. If you launch EiC with the -r
switch, it is initialized by interpreting all the commands previously recorded in EiChist.lst
. To reinitialize EiC without wiping out the old EiChist.lst
file, you can also combine these two switches with -nr
. Using this feature of EiC, you can continue from the state you left last time. Edit EiChist.lst
manually to start EiC from a customized state, or start EiC in interactive mode with the -R
switch. EiC asks about re-entering every command from EiChist.lst
by choosing either Y
(yes), N
(no), or E
(edit). You can drop or edit previous C code statements entered at the EiC interactive prompt one by one.
Another distinct feature of EiC is that it is pointer safe. Anytime you try to violate a stack or heap memory limits through pointers, EiC catches them and throws errors. Enter the C statements shown in Listing 4 in interactive mode, interpret pointertest.c
in batch mode, or run the file in scripting mode and see how EiC throws errors and stops on the first pointer violation.
Listing 4: pointertest.c |
01 #include <stdio.h> 02 #include <stdlib.h> 03 04 int main() 05 { 06 07 int * pPtr, iArray[100]; 08 09 printf(" pPtr : 0x%0x\n", pPtr); 10 printf(" iArray[20] : %d\n", iArray[20]); 11 12 pPtr = malloc(57); 13 14 pPtr[79] = 7; 15 printf(" pPtr[79] : %d\n", pPtr[79]); 16 17 iArray[-8] = 6; 18 printf(" iArray[-8] : %d\n", iArray[-8]); 19 20 return 0; 21 22 } |
Before building and distributing a final version, you can verify your C programs against various memory limits through EiC's pointer safety feature. By doing this, you will save a lot of time debugging very hard-to-find and hard-to-fix memory and pointer bugs. The pointer safety feature of EiC is turned on by default, but you can turn it off feature with the modifier unsafe
. So, if you replace int *pPtr
by int * unsafe pPtr
in the pointertest.c
file, EiC proceeds without any errors regarding pPtr[79]
. The unsafe pointer feature in EiC is useful when you interact with external, compiled C components.
Now comes the big daddy of all the C interpreters. CINT is the C and C++ code interpretation component of the object-oriented data analysis package ROOT [5], although it can be used as a standalone application. According to the CINT man page, CINT covers a whopping 95% of ANSI C and 90% of C++ features. CINT also provides features similar to those of GDB to debug the interpreted source code. CINT scripts have the ability to communicate with compiled components and external applications.
If you don't mind the overhead of many extra components, you can install CINT by typing sudo apt-get install root-system
in a text console. Then, type cint -help
, and you should see some help text.
Alternatively, to build and install the latest version of CINT from source, install the Readline library and sources with the command:
sudo apt-get install libreadline-dev
Next, download the latest source tarball of ROOT from its download page [6] and issue the following commands:
tar -zxvf root-version.source.tar.gz cd root/cint
Finally, to build CINT, enter:
./configure make
If the compilation goes well, you should see executables like cint
and makecint
in the bin
subdirectory. To set executable and library search paths, as well as CINT-specific settings, you should add the following environmental variables in .bashrc
:
CINTBASE=<path of cint directory> export CINTSYSDIR=$CINTBASE export PATH=$CINTBASE/bin:$PATH export LD_LIBRARY_PATH=$CINTBASE/lib:$LD_LIBRARY_PATH export MANPATH=$CINTBASE/doc:$MANPATH
Now type sudo ldconfig
in a text console to configure dynamic linker run-time bindings.
To see CINT in action, create a file with the contents shown in Listing 5 then type cint hellocint.cpp
in a text console. To interpret C and C++ code organized in multiple files, you have to provide CINT with a list of source files; the last file in the list must contain the main()
function.
Listing 5: hellocint.cpp |
01 #include <iostream> 02 using namespace std; 03 04 int main() 05 { 06 07 cout << endl 08 <<" Hello to FLOSS from CINT!!!" 09 << endl; 10 11 return 0; 12 13 } |
To run the stack example described earlier, type cint stack.c test.c
. If CINT can't find a main()
function or any other error, it starts an interactive session, in which you can issue various CINT commands.
To use the interpreted code debugging feature of CINT, put a breakpoint on a desired function and use the -b
option along with the list of source code files. Now examine or change the program execution state through various debugging options mentioned in CINT the man page. You can also see the debugging options by entering h
at the CINT interpreter prompt.
You can use the functions provide by EiC to embed the functionality for C code interpretation in an external C application. Run a C source file with the function EiC_run(int argc, char **argv)
, where argc
represents the number of arguments passed and argv
represents an array of strings consisting of a C source file name along with other command-line arguments. To run a C code file named myeic.c
use:
char *argv = {"myeic.c", ...}; int argc = sizeof(argv)/sizeof(char *); EiC_run(argc, argv);
Also, you can pass C or preprocessor commands to EiC with the function EiC_parseString(char *command, ...)
. To build apps with these functions, include eic.h
, found in the include
subdirectory of the EiC source directory and link with the libeic
and libstdClib
libraries in the lib
subdirectory of the EiC source directory. To play more with the embedding capabilities of EiC, follow embedEiC.c
, which is found in the main/example
subdirectory of the EiC source directory.
CINT is extensible through external functionalities that are coded in C and C++, which means you can create customized versions of CINT that contain your external C and C++ functionalities as added features. This feature of CINT should be used to interpret source code with custom extensions, without the need to supply headers and additional source files.
To embed your external functionalities in CINT, you need Makecint. Makecint is an interpreter compiler that is built during the build process for CINT. Makecint automates the process of embedding external functionalities coded in C and C++ implementations, and it generates the necessary wrapper code automatically to create your customized version of CINT.
To try your luck with this powerful feature of CINT, create the files shown in Listings 6 and 7 and issue the following command in a text console:
makecint -mk Makefile -o mycint -H mycint.hpp -C++ mycint.cpp
The -mk
switch sets the name of the Makefile generated by Makecint, -o
sets the name of the customized version of CINT, and -H
and -C++
are switches that let you mention the headers and sources containing the external functionalities you want to embed in CINT. For more information about the various options supported by Makecint, consult the man page.
If you examine the directory listing, you can see various additional source and object files created by Makecint to build your customized version of CINT. Now, to build the customized interpreter, just type make
in the text console to create an executable mycint
in the current directory.
To test your newly created customized version of CINT, run the code shown in Listing 8 by typing ./mycint testmycint.cpp
in the text console.
Your external functionalities are now included in the code interpreter itself.
Listing 6: mycint.hpp |
01 #ifndef MYCINT_HPP 02 #define MYCINT_HPP 03 04 #include <iostream> 05 using namespace std; 06 07 class CMakeCintDemo { 08 09 private: 10 11 int iState; 12 13 public: 14 15 CMakeCintDemo(); 16 17 CMakeCintDemo(int iValue); 18 19 ~CMakeCintDemo(); 20 21 void getState() const; 22 23 }; 24 25 #endif |
Listing 7: mycint.cpp |
01 #include "mycint.hpp" 02 03 CMakeCintDemo::CMakeCintDemo() { 04 05 cout << endl 06 << " CMakeCintDemo::CMakeCintDemo():this = " 07 << hex 08 << this 09 <<"." 10 << endl; 11 12 } 13 14 CMakeCintDemo::CMakeCintDemo(int iValue):iState(iValue) { 15 16 cout << endl 17 << " CMakeCintDemo::CMakeCintDemo(int iValue):this = " 18 << hex 19 << this 20 << dec 21 << ", iValue = " 22 << iState 23 << "." 24 << endl; 25 26 } 27 28 CMakeCintDemo::~CMakeCintDemo() { 29 30 cout << endl 31 << " CMakeCintDemo::~CMakeCintDemo():this = " 32 << hex 33 << this 34 << "." 35 << endl; 36 37 } 38 39 void CMakeCintDemo::getState() const { 40 41 cout << endl 42 << " CMakeCintDemo::getState():this = " 43 << hex 44 << this 45 << dec 46 << ", iState = " 47 << iState 48 << "." 49 << endl; 50 51 } 52 |
Listing 8: testmycint.cpp |
01 int main() 02 { 03 04 CMakeCintDemo cMCT1, cMCT2(13); 05 06 cMCT2.getState(); 07 08 } |
C and C++ interpreters provide a big productivity boost. These interpreters are very helpful for C and C++ education, as well as for quick prototyping and experimentation with C and C++ programs.
EiC and CINT go beyond mere interpretation and provide extra functionalities, such as tracing down the memory limit violations at the coding stage, embedding C code interpretation into external applications, and communicating with compiled C and C++ applications. All these code interpreters can make C and C++ development more productive, more flexible, and more enjoyable.
INFO |
[1] Tcc homepage: http://bellard.org/tcc/
[2] Code for this article: http://www.linux-magazine.com/Resources/Article-Code [3] PicoC project page: http://code.google.com/p/picoc/ [4] EiC project page: http://eic.sourceforge.net [5] ROOT: http://root.cern.ch/ [6] ROOT download page: http://root.cern.ch/drupal/content/downloading-root |
AUTHOR |
Ankur Kumar Sharma is a software developer and researcher who likes to play and croon classic rock songs on his guitar. He also enjoys reading self-help books, writing, and exploring all the interesting things in life. He blogs at http://www.richnusgeeks.com. |