Variables: C DatatypesLisp variables may be passed to or received from C style functions that take any of the standard C datatypes as parameters. C datatypes include byte/char, short, int, float, and double. Corman Lisp transparently takes care of much of the necessary casting and conversion between Lisp and C variables.
C
int width = 0;
int height = 0;
Lisp
(setf width 0)
(setf height 0)
3.3) Variables: C Arrays and Structures
Things begin to get interesting when structures and arrays are used. Pure Lisp arrays are not equivalent to C arrays and therefore it is not possible to pass a Lisp array to a C function. If a C function takes an array a C style array must be created from within Lisp.
C
static GLfloat v0[] = { -1.0f, -1.0f, 1.0f };
When using C, arrays may be created on the stack as shown in the C code above. When a C array is created in Lisp, it is always created on the heap - meaning it must be created using malloc. Because Lisp is garbage collected there is no need to explicitly free a variable created using malloc. The garbage collector will automatically free the memory for v0 when the variable goes out of scope. Although there is nothing stopping the reader from explicitly freeing a variable using the function free.
Lisp
(setf v0 (ct:malloc (ct:sizeof ‘(:single-float 3))))
A C array may be initialized when it is created. In Lisp because C arrays are created on the heap, assignment must take place as a separate step.
Lisp
(setf (ct:cref (:single-float 3) v0 0) -1.0)
(setf (ct:cref (:single-float 3) v0 1) -1.0)
(setf (ct:cref (:single-float 3) v0 2) 1.0)
cref (the ct: prefix identifies the package that contains cref) is used for accessing a C style array, or structure. Therefore, looking at the statements above:
ct:cref - accessing an array or structure.
(:single-float 3) - in this case, it is an array of 3 floats.
v0 - the name of the array we previously created using malloc.
0 - The index into the array
The Lisp implementation seems backward compared to the C-style the reader is probably used to, e.g v0[0] = -1. But that’s how it is done in CCL. It is possible to improve upon this using macros
The previous code described assignment. Retrieving a value is much the same, but without the setf.
Lisp
(ct:cref (:single-float 3) v0 0)
-1.0
The code for accessing a C struct is described below.
C
typedef struct {
Sint16 x;
Sint16 y;
Uint16 w;
Uint16 h;
} SDL_Rect;
Lisp
(setf a-rectangle (ct:malloc (ct:sizeof ’sdl:SDL_Rect)))
(setf (ct:cref sdl:SDL_Rect a-rectangle sdl::x) 120)
(setf (ct:cref sdl:SDL_Rect a-rectangle sdl::x) 200)
(setf (ct:cref sdl:SDL_Rect a-rectangle sdl::w) 64)
(setf (ct:cref sdl:SDL_Rect a-rectangle sdl::h) 64)
The macro with-c-struct makes working with structs a lot easier, as shown below:
(with-c-struct (x rectangle sdl:SDL_Rect)
(setf
sdl::x 120
sdl::y 200
sdl::w 64
sdl::h 64))
An important consideration is that the Lisp sizeof function can only be passed defined types, for example sdl:SDL_Rect. Calling sizeof on a variable such as a-rectangle will fail.
3.4) Functions
Calling C style functions in Lisp is straightforward.
C
SDL_Init( SDL_INIT_VIDEO );
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
Lisp
(SDL_Init SDL_INIT_VIDEO)
(SDL_GL_SetAttribute SDL_GL_RED_SIZE 5)
3.5) Callbacks
defun-c-callback is used to create a function callback in Lisp. As an example, the C prototype for the SDL function SDL_AddTimer is shown below. SDL_AddTimer takes a function as one of its parameters and calls that function at the specified interval.
C callback function prototype
typedef Uint32 (*SDL_NewTimerCallback)(Uint32 interval, void *param);
Lisp callback function
(ct:defun-c-callback timer-callback ((interval SDL:Uint32) (param (:void *)))
(fformat “Yup, timer Fired”)
(values interval))
C SDL_AddTimer prototype definition
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param);
Lisp code using a callback
(setf param (ct:malloc (ct:sizeof :LONG)))