0

我正在使用 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过程的___arg1NULL。无论我尝试什么,我都无法在 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 算法”中所示. 这样就不需要指向指针的指针。

4

1 回答 1

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 越过边界到 C 时,它会将Scheme 值#f转换为 C 值NULL(即)。0

这与:

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. 这与将值作为第一个参数传递给非常不同,它看起来像这样:NULLpg_render_list_create_item

pg_render_list_create_item(ui_render_list, UI_ID_TEXT, ui_node);

注意没有 & 符号&;这是一个关键的区别。

我还没有花时间尝试自己在 Gambit-C 中编写你的示例,但我想你可以实现你想要的效果的一种方法是自己分配接收内存(通过挂接到mallocFFI 中的函数),然后一旦你分配了内存,你就会有一个地址,你可以将它作为第一个参数传递给pg-render-list-item.

于 2013-03-05T13:02:33.037 回答