Thursday, March 10, 2005


Communicating with the outside world

Many languages provide something a mechanism to call into external libraries. I thought I'd mention two ways this is done, one in Lisp and another in Python. The example I am using for Python uses the dynamic libraries package. There is also an excellent package called ctypes that works well under Windows, Linux and MacOS X.

I have a C library that works with Directed Acyclic Word Graphs, specifically, the type of file used to store words for the Hasbro Scrabble game. I use the DAWG for automated cryptogram solvers as well. The C library is available at If you want to play with this, the dictionary file is available at The 3 functions that you would care about are:

int load_dawg(char *filename); // Load the DAWG into memory
int match(char *word); // Returns non-zero if WORD is a valid word in the dawg
int longest_match(char *word); // Returns the length of the longest word found at the beginning
// of the string passed in. (MARKFOOBAR would return 4,
// since MARK is valid, but MARKF isn't)

Under Linux, I compiled this into a shared library with the following commands:

gcc -fPIC -c dawg.c
gcc -shared -Wl,-soname, -o dawg.o

Now, from Python, I can use and to invoke these functions:

import dl"")

def dawg_open(filename):
return"load_dawg", filename)

def dawg_match(word):
return"match", word)

def dawg_longest_match(word):
return"longest_match", word)


print "FOO=%d" % (dawg_match("FOO"))
print "AA=%d" % (dawg_match("AA"))
print "A=%d" % (dawg_match("A"))
print "MARK=%d" % (dawg_match("MARK"))
print "longest PRINTFOOBAR=%d" % (dawg_longest_match("PRINTFOOBAR"))

As you can see, I wrapped these calls with convenience methods. I do the same thing in the Lisp code below, which uses the Universal Foreign Function Interface.

(uffi:load-foreign-library #p"" :module "dawg" :supporting-libraries '("c"))

(uffi:def-function ("load_dawg" dawg-load-c) ((name :cstring)) :module "dawg" :returning :int)
(uffi:def-function ("match" dawg-match-c) ((word :cstring)) :module "dawg" :returning :int)
(uffi:def-function ("longest_match" dawg-longest-match-c) ((word :cstring))
:module "dawg" :returning :int)

(defun dawg-load (filename)
(let* ((c-filename (uffi:convert-to-cstring filename))
(result (dawg-load-c c-filename)))
(uffi:free-cstring c-filename)

(defun dawg-match (word)
(let* ((c-word (uffi:convert-to-cstring word))
(result (dawg-match-c c-word)))
(uffi:free-cstring c-word)

(defun dawg-longest-match (word)
(let* ((c-word (uffi:convert-to-cstring word))
(result (dawg-longest-match-c c-word)))
(uffi:free-cstring c-word)

(defun run-test ()
(dawg-load "twl98.daw")
(format t "FOO=~A~%" (dawg-match "FOO"))
(format t "AA=~A~%" (dawg-match "AA"))
(format t "A=~A~%" (dawg-match "A"))
(format t "MARK=~A~%" (dawg-match "MARK"))
(format t "longest PRINTFOOBAR=~A~%" (dawg-longest-match "PRINTFOOBAR")))

Again, I defined convenience methods, mostly because I needed to allocate and free the C strings. It's really nice when a language lets you use external library without having to write any glue code in C or some other language.

Comments: Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?