RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Using Gambit-C Scheme to Create Small, Efficient Native Applications : Page 3

When you need to build small, native standalone applications, Gambit-C Scheme provides a high-level dynamic language with good runtime and memory performance, as well as good deployment options.


The "Hello Command Line" Example

The previous section explained how to define functions in Scheme. This section demonstrates how to access command-line arguments and generate a test standalone executable. The following code snippet uses the function print-arg-list, which contains at least two surprises if this is your first time using Scheme:
  1. The function calls itself recursively (as did the function fact you saw in the last section) to implement a loop over a list of argument values.
  2. It uses another function that is defined inside its definition.
(define (print-arg-list arg-list)

  (define (print-arg arg) ; define the inner utility function print-arg
    (display "Next command-line-argument: ")
    (display (car arg-list))

  ;; this is the start of the function print-arg-list:
  (if (pair? arg-list) ; check that arg-list is a list with at least one argument
      (let ()
        (print-arg (car arg-list))
        (print-arg-list (cdr arg-list))))) ; make a recursive call

The only reason that I split out the separate inner function print-arg was to show you an example of nested functions. Nesting function definitions like this is a good technique when a small utility function is used by only one other function. The inner function is not visible outside the scope of the outer function. Using a nested function makes sense when it is used more than once in an outer function definition.

When I am interactively coding, I write and debug small functions as I write them to catch mistakes earlier. In the case of nested functions, I usually write and debug them before copying them into the definition of another function that uses them.

You can test the function print-arg-list using gsi:

> (print-arg-list '("-i" "input.dat" "-o" "output.dat" 1 2 3))
Next command-line-argument: -i
Next command-line-argument: input.dat
Next command-line-argument: -o
Next command-line-argument: output.dat
Next command-line-argument: 1
Next command-line-argument: 2
Next command-line-argument: 3

You have seen that top-level Scheme expressions typed in gsi are evaluated immediately:

> (display "Hello\n")

If you put top-level Scheme expressions in a Scheme source file and compile it to a native application, then these top-level expressions are also immediately executed when the application runs.

The gsc compiler generates a separate C file ending with _.c. The generated .c files contain a main C function. For the "Hello Command Line Arguments" test program, I use the print-arg-list function and the following expression:

(let* ((args (command-line))
       (in-file (member "-i" args))
       (out-file (member "-o" args))
       (arg-hash-table (make-table)))
  ;; let's start by printing the command line arguments:
  (print-arg-list args)
  ;; check to see if an argument is "-help":
  (if (member "-help" args) (print-help))
  ;; now print out input and output file names, if they are supplied:
  (if (and
       (> (length in-file) 1))
      (let ()
        (display "Input file: ") (display (cadr in-file)) (newline)))
  (if (and
       (> (length out-file) 1))
      (let ()
        (display "Output file: ") (display (cadr out-file)) (newline))))

This expression uses the built-in Gambit-C specific function command-line, which returns command-line arguments as a list of strings. It also uses the Scheme function member, which takes two arguments: an expression and a list of expressions. The expression searches the list for an element equal to the first argument.

Here are some examples of member:

> (define test-list '(1 2 3 4 5 "cat" 3.14159 100 101))
> (member 3 test-list)
(3 4 5 "cat" 3.14159 100 101)
> (member "cat" test-list)
("cat" 3.14159 100 101)
> (member "not in list" test-list)

Note that member returns a sub-list starting with the matching list element. The function length returns the number of elements in a list:

> (length test-list)

An example program you will develop later requires you to process command arguments.

Creating an Executable File

When creating standalone executables, be aware that the Gambit-C gsc compiler compiles Scheme code to C; the generated C code is compiled and linked against the Gambit-C libraries. The location of these files is different for OS X, Linux, and Windows. When I started writing this article, using version 4.4.4 of Gambit-C, compiling and linking Scheme code into a native executable took several steps and was platform dependent because you had to know the location of the include files and library files for Gambit-C. With the release of Version 4.5.0, the compiler has a new option, -exe, that removes the platform dependencies and allows you to compile to C, compile the generated C code, and link it all with one command.

The code directory hello-command-line-args in the source code download contains both the source file hello-command-line.scm and a makefile. Here is a listing of the makefile:


   rm -f *~
   rm -f */*~
   rm -f \#*
   rm -f */\#*
   rm -f *.c
   rm -f hello-command-line

   gsc -exe hello-command-line.scm

The make target clean removes all files from the working directory except for the Scheme source file and the makefile. The make target app builds a standalone executable with no other dependencies. Here is an attempt at running the hello command-line executable:

markw$ ./hello -help
Next command-line-argument: ./hello
Next command-line-argument: -help
Hello Command Line (native) command line arguments:
   -help           -- to print help message
   -i   -- to define the input file name
   -o   -- to specify the output file name
markw$ ./hello "this is a test" 1 2 3
Next command-line-argument: ./hello
Next command-line-argument: this is a test
Next command-line-argument: 1
Next command-line-argument: 2
Next command-line-argument: 3
markw$ ./hello -i input.dat -o output.dat
Next command-line-argument: ./hello
Next command-line-argument: -i
Next command-line-argument: input.dat
Next command-line-argument: -o
Next command-line-argument: output.dat
Input file: input.dat
Input file: output.dat

The next section demonstrates another example that processes input text files.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date