我正在使用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中获得有效的(指针(指针))。
我有什么遗漏吗?
=============================================== =============
完整的方案示例:
(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,但我认为(指针(指针))可能会捕获它。即使传递绑定到某些内容的变量也会导致NULL值。我通过在(c-declare)中创建一个简单打印指针地址的函数来测试它:
如果您想查看我的完整包装器,您可以look here at this commit
==========================================
如何获取(指针(指针))仍然有效的问题。但我认为为了更快的结果,以及更好的与其他语言的互操作性,我将重写我的C列表宏来定义一个列表结构,然后包含指向列表头/尾的指针,如“使用C掌握算法”中所示。这样就没有必要指向指针了。
答案 0 :(得分:1)
也许我误解了,但是当你有
时(define ui-render-list #f)
然后我会认为表达式为:
(pg-render-list-item-add ui-render-list model-data)
表现得像:
“使用实际参数
pg-render-list-item-add
和#f
表示的任何内容调用model-data
。
然后,Gambit-C FFI将Scheme值#f
转换为C值NULL
(即0
),当它跨越从Scheme到C的边界时。
这与以下内容截然不同:
“使用地址
pg-render-list-item-add
调用<addr>
,无论model-data
表示什么” 其中<addr>
意味着是pg-render-list-item-add
创建的项目的接收者。
C声明:
pg_render_list_create_item(&ui_render_list, UI_ID_TEXT, ui_node);
获取ui_render_list
变量的地址(例如,可以在堆栈上分配),并将该地址传递给pg_render_list_create_item
。 非常不同于传递值NULL
作为pg_render_list_create_item
的第一个参数,它看起来像这样:
pg_render_list_create_item(ui_render_list, UI_ID_TEXT, ui_node);
注意没有&号&
;这是一个至关重要的区别。
我还没有花时间尝试在Gambit-C中自己编写你的例子,但我想你可以通过一种方式来实现你想要的效果就是自己分配接收内存(通过挂钩{{1 FFI中的函数,然后一旦你分配了内存,你就会有一个地址可以作为第一个参数传递给malloc
。