无法获得指向在 Gambit-C 方案的 FFI 中工作的指针的指针
Posted
技术标签:
【中文标题】无法获得指向在 Gambit-C 方案的 FFI 中工作的指针的指针【英文标题】:Can't get pointers to pointers working in Gambit-C scheme's FFI 【发布时间】:2013-02-16 16:37:58 【问题描述】:我正在使用 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)中创建一个简单地打印指针地址的函数来测试这一点:
如果您想查看我的完整包装,您可以look here at this commit
============================================
如何让(指针(pointer))工作的问题仍然存在。但我认为,为了更快的结果,以及与其他语言更好的互操作性,我将重写我的 C 列表宏来定义一个列表结构,该结构将包含指向列表头/尾的指针,如“掌握 C 算法”中所示.这样就不需要指向指针的指针了。
【问题讨论】:
你能举一些例子来说明你是如何调用render-list-add-item
的吗?特别是我很好奇您如何提前设置内存然后获取它的地址,(类似于 C 中的 &
运算符)
对问题进行了编辑,以便我可以回复带有注释的代码。另外,我达到了字符数限制。
【参考方案1】:
也许我误解了,但是当你有
(define ui-render-list #f)
那么我会认为表达式:
(pg-render-list-item-add ui-render-list model-data)
会表现得像:
“使用实际参数
#f
和model-data
表示的任何内容调用pg-render-list-item-add
。
然后,当 Gambit-C FFI 越过边界从 Scheme 到 C 时,它会将 Scheme 值 #f
转换为 C 值 NULL
(即0
)。
这与:
“使用 地址
<addr>
和model-data
表示的任何内容调用pg-render-list-item-add
” 其中<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 中编写你的示例,但我想你可以实现你想要的效果的一种方法是自己分配接收内存(通过挂钩到 malloc
FFI 中的函数),然后在分配内存后,您将获得一个地址,您可以将其作为第一个参数传递给 pg-render-list-item
。
【讨论】:
这很好地解释了这个问题。我认为在 FFI 包装器中分配一个新指针并不能完全解决我的问题,因为我看不到如何通过 API 返回它,除非我返回 (malloc
返回的值;据我了解,这将代表从那时起列表的头部。这取决于您想要什么:对 Scheme 代码不可见的本机列表结构(malloc
加上您的 API 可以提供的内容),或者由 Scheme 列表支持的 GUI 列表结构(如果可能,这将需要更多的扭曲完全)。在Scheme workshop 2008 上有关于计划 FFI 的论文,请参阅第 3 节;他们可能会对此进行更多解释。以上是关于无法获得指向在 Gambit-C 方案的 FFI 中工作的指针的指针的主要内容,如果未能解决你的问题,请参考以下文章
节点 ffi napi,调用返回 true 但指针中没有结果