无法获得指向在 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过程的___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)中创建一个简单地打印指针地址的函数来测试这一点:

如果您想查看我的完整包装,您可以look here at this commit

============================================

如何让(指针(pointer))工作的问题仍然存在。但我认为,为了更快的结果,以及与其他语言更好的互操作性,我将重写我的 C 列表宏来定义一个列表结构,该结构将包含指向列表头/尾的指针,如“掌握 C 算法”中所示.这样就不需要指向指针的指针了。

【问题讨论】:

你能举一些例子来说明你是如何调用render-list-add-item的吗?特别是我很好奇您如何提前设置内存然后获取它的地址,(类似于 C 中的 &amp; 运算符) 对问题进行了编辑,以便我可以回复带有注释的代码。另外,我达到了字符数限制。 【参考方案1】:

也许我误解了,但是当你有

(define ui-render-list #f)

那么我会认为表达式:

(pg-render-list-item-add ui-render-list model-data)

会表现得像:

“使用实际参数#fmodel-data 表示的任何内容调用pg-render-list-item-add

然后,当 Gambit-C FFI 越过边界从 Scheme 到 C 时,它会将 Scheme 值 #f 转换为 C 值 NULL(即0)。

这与:

“使用 地址 &lt;addr&gt;model-data 表示的任何内容调用 pg-render-list-item-add” 其中&lt;addr&gt;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);

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

我还没有花时间尝试自己在 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 但指针中没有结果

为啥我的 FFI 函数的第二次调用无法匹配字符串比较?

GHCi 不适用于 FFI 出口声明/共享库

在 Node-FFI 中使用 SendInput

使用 module-info.java 时无法加载 FFI 提供程序

jnr-ffi:有没有从头文件到java代码的生成工具