我正在使用 Gambit-C 包装我的图形引擎 API,并且到目前为止在 FFI 方面取得了成功。今天我遇到了一个我无法轻易解决的新问题。
我在C中有这样的结构:
typedef struct render_list_rec
{
long render_id;
render_node* node;
struct render_list_rec* next;
} render_list;
在 C 语言中,我还有一系列由宏定义的函数,用于添加常见的列表行为。然后最终看起来像这样:
void render_list_item_add(render_list_item **list, render_list_item* elem);
在 C 中,您可以有一个为 NULL 的 render_list_item*,但可以将其传递给此函数的第一个参数,它实际上会为您创建列表的头部。
我的问题是我无法让这种行为在 Gambit-C 的 FFI 中起作用。我最终创建了这样的东西:
(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"))
当我运行它时,它会出现段错误。经调查,render-list-add-item过程的___arg1为NULL。无论我尝试什么,我都无法在 FFI 中获得有效的(指针(pointer))。
我有什么遗漏的吗?
==================================================== ==========
一个完整的方案示例:
(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)
编译:
gsc -o test -exe test.scm
给出输出:
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>)
==================================================== ==========
编辑:
Felix:你能举一些例子来说明你是如何调用 render-list-add-item 的吗?
用于此的 C 代码如下所示:
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);
它是基于sglib的列表实现。当这些传递一个指向空指针的指针时,如上所述,它会创建一个新的列表项作为列表的头部,以便 *ui_render_list 指向它。
方案代码看起来像这样(从内存中输入):
(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))
希望有类似的行为。从查看文档看来,在 C API 中有 #f 会转换为 NULL,但我认为 (pointer (pointer)) 可能会抓住这一点。即使传递绑定到某物的变量也总是会导致 NULL 值。我通过在(c-declare)中创建一个简单地打印指针地址的函数来测试这一点:
如果你想看到我的完整包装器在运行,你可以在这里查看这个提交
===========================================
如何让(指针(pointer))工作的问题仍然存在。但我认为,为了更快的结果,以及与其他语言更好的互操作性,我将重写我的 C 列表宏来定义一个列表结构,该结构将包含指向列表头/尾的指针,如“掌握 C 算法”中所示. 这样就不需要指向指针的指针。