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 http://www.wutka.com/download/dawg.c. If you want to play with this, the dictionary file is available at http://www.wutka.com/download/twl98.daw. 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,libdawg.so.1 -o libdawg.so.1 dawg.o


Now, from Python, I can use dl.open and dl.call to invoke these functions:

import dl

dawglib=dl.open("libdawg.so.1")

def dawg_open(filename):
return dawglib.call("load_dawg", filename)

def dawg_match(word):
return dawglib.call("match", word)

def dawg_longest_match(word):
return dawglib.call("longest_match", word)

dawg_open("twl98.daw")

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"libdawg.so.1" :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)
result))

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

(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)
result))

(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?