I'm in the process of wrapping my graphics engine API with Gambit-C and have been successful so far with the FFI. Today I ran into a new problem that I can't easily get past.
I have a structure like this in C:
typedef struct render_list_rec
{
long render_id;
render_node* node;
struct render_list_rec* next;
} render_list;
In C, I also have a series of functions that get defined by macros to add common list behaviors. Then end up looking something like this:
void render_list_item_add(render_list_item **list, render_list_item* elem);
In C, you can have a render_list_item* that is NULL, but can pass it to the first parameter of this function and it will essentially create the head of the list for you.
My problem is that I cannot get this behavior to work in Gambit-C's FFI. I end up creating something like this:
(c-define-type render-list* (pointer (struct "render_list_rec")))
(c-define-type render-list** (pointer (pointer (struct "render_list_rec"))))
(define render-list-add-item (c-lambda (render-list** long render-node*) render-list* "render_list_add_item"))
When I run this it segfaults. Upon investigation, ___arg1 of the render-list-add-item proceedure is NULL. No matter what I try, I cannot get a valid (pointer (pointer)) in the FFI.
Is there something I'm missing with this?
============================================================
A complete scheme example:
(c-declare #<<c-decl-end
#include <stdio.h>
#include <stdlib.h>
typedef struct test_rec
{
int i;
} test_rec;
void pointer_test(struct test_rec** in_number)
{
if (in_number == NULL) {
fprintf(stdout, "pointer_test is NULL\n");
}
}
test_rec* new_pointer_test(void)
{
return malloc(sizeof(struct test_rec));
}
c-decl-end
)
(c-define-type test-rec* (pointer (struct "test_rec")))
(define c-pointer-test (c-lambda ((pointer test-rec*)) void "pointer_test"))
(define c-new-pointer-test (c-lambda () test-rec* "new_pointer_test"))
(define test-rec->i-set! (c-lambda (test-rec* int) void "___arg1->i = ___arg2;"))
(display "About to run test with #f ...") (newline)
(define the_false #f)
(c-pointer-test the_false)
(display "About to run test with 1 ...") (newline)
(define number_one (c-new-pointer-test))
(test-rec->i-set! number_one 1)
(c-pointer-test number_one)
Compile with:
gsc -o test -exe test.scm
Gives output:
About to run test with #f ...
pointer_test is NULL
About to run test with 1 ...
*** ERROR IN ##execute-program -- (Argument 1) Can't convert to C pointer
(c-pointer-test '#<|struct test_rec*| #2 0x28d3fc0>)
============================================================
EDIT:
Felix: Can you give some examples of how you are invoking render-list-add-item
The C code for this looks something like this:
pg_render_list *ui_render_list = NULL;
pg_render_node *ui_node = pg_font_generate_text_string(app_font, L"Lacunarity:", ui_text_material);
pg_render_list_create_item(&ui_render_list, UI_ID_TEXT, ui_node);
Its a list implementation based off of sglib. When these passed a pointer that points to a null pointer,as above, it creates a new list item as the head of the list so that *ui_render_list will point to it.
The scheme code looked something like this (typed from memory):
(define ui-render-list #f)
(letrec ((model-data (pg-model-data-read-binary model-filepath))
(model-node (pg-render-node-create-fom-model model-data GL_STATIC_DRAW)))
(pg-render-list-item-add ui-render-list model-data))
The hope was to have similar behavior. It appears from looking at the documentation that having a #f in the C API tranlates to NULL, but I thought the (pointer (pointer)) might catch that. Even passing variables that are bound to something always led to a NULL value. I tested this by creating a function in a (c-declare) that simply printed the address of the pointer:
If you want to see my full wrappers in action, you can look here at this commit
==========================================
The question of how to get (pointer (pointer)) working still stands. But I think that for quicker results, and better interoperability with other languages, I'm going to rewrite my C list macros to define a list structure that will then contain pointers to the list head/tail as seen in "Mastering Algorithms with C". That way pointer to pointers won't be necessary.