[plt-scheme] Problems with ffi

From: troels knak-nielsen (troelskn at gmail.com)
Date: Sat Mar 21 12:34:54 EDT 2009

Hi,

For the past days, I've been strugling with ffi, trying to map a
complex struct, and I was hoping for some help on this. My C is a bit
rusty, and I'm fairly new to Scheme as well, so you might say I'm a
bit out of my league, but I'm not going to back down now.

I'm working on creating bindings for libmysqlclient (There's a
git-repo at https://github.com/troelskn/mr-mysql/tree). I didn't write
all the code myself, but I have worked through it and understand it
quite well thus far. My problem now, is how to map a rather
complicated struct (MYSQL_BIND). This struct contains a number of
pointers to places where the C-code will write data back to, and I'm
lost in the various mechanisms for pointer-handling in ffi.

http://dev.mysql.com/doc/refman/5.1/en/mysql-stmt-execute.html shows
how to use the api in C. I'm basically trying to accomplish this part
(slightly simplified):

#define MYSQL_TYPE_STRING 254
#define STRING_SIZE 50
MYSQL_BIND    bind;
char          str_data[STRING_SIZE];
unsigned long str_length;
my_bool       is_null;

/* STRING PARAM */
bind.buffer_type= MYSQL_TYPE_STRING;
bind.buffer= (char *)str_data;
bind.buffer_length= STRING_SIZE;
bind.is_null= 0;
bind.length= &str_length;

The definition of MYSQL_BIND looks like this (from mysql.h):

typedef struct st_mysql_bind
{
  unsigned long	*length;          /* output length pointer */
  my_bool       *is_null;	  /* Pointer to null indicator */
  void		*buffer;	  /* buffer to get/put data */
  /* set this if you want to track data truncations happened during fetch */
  my_bool       *error;
  enum enum_field_types buffer_type;	/* buffer type */
  /* output buffer length, must be set when fetching str/binary */
  unsigned long buffer_length;
  unsigned char *row_ptr;         /* for the current data position */
  unsigned long offset;           /* offset position for char/binary fetch */
  unsigned long	length_value;     /* Used if length is 0 */
  unsigned int	param_number;	  /* For null count and error messages */
  unsigned int  pack_length;	  /* Internal length for packed data */
  my_bool       error_value;      /* used if error is 0 */
  my_bool       is_unsigned;      /* set if integer type is unsigned */
  my_bool	long_data_used;	  /* If used with mysql_send_long_data */
  my_bool	is_null_value;    /* Used if is_null is 0 */
  void (*store_param_func)(NET *net, struct st_mysql_bind *param);
  void (*fetch_result)(struct st_mysql_bind *, MYSQL_FIELD *,
                       unsigned char **row);
  void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *,
		      unsigned char **row);
} MYSQL_BIND;

What I have tried in scheme, is this:

#lang scheme/base
(require (lib "foreign.ss"))

(unsafe!)

(define libmysqlclient (ffi-lib "libmysqlclient"))

(define _my-bool
  (make-ctype _byte
              (lambda (x) (if x 1 0))
              (lambda (x) (not (eq? 0 x)))))

(define-cstruct _mysql-bind
  ((length _pointer)
   (is-null (_ptr io _my-bool))
   (buffer _pointer)
   (error _pointer)
   (buffer-type _uint)
   (buffer-length _ulong)
   (row-ptr _string)
   (offset _ulong)
   (length-value _ulong)
   (param-number _uint)
   (pack-length _uint)
   (error-value _my-bool)
   (is-unsigned _my-bool)
   (long-data-used _my-bool)
   (is-null-value _my-bool)
   (store-param-func _pointer)
   (fetch-result _pointer)
   (skip-result _pointer)))

(printf "testing~%")

(define buffer (malloc	50))
(define str-length (malloc _ulong))
(define is-error (malloc _my-bool))
(define binding (make-mysql-bind
                   str-length ; length = &str_length;
                   #f ; is_null = 0;
                   buffer ; buffer = (char *)str_data;
                   is-error
                   254 ; buffer_type = MYSQL_TYPE_STRING;
                   50 ; buffer_length = STRING_SIZE;
                   0
                   0
                   0
                   0
                   0
                   0
                   0
                   0
                   0
                   0
                   0
                   0))

(print binding)

When I run the above script, I get the following error:

    testing
    ptr-set!: expects type <C-type> as 2nd argument, given:
#<procedure:mysql-bind-buffer-type>; other arguments were:
#<cpointer:mysql-bind> abs 8 #<cpointer>

     === context ===
    make-mysql-bind

What does that mean, and what am I doing wrong?

If it matters, I'm using MzScheme v4.1.4.3 (compiled from trunk)

--
troels


Posted on the users mailing list.