Не удается получить указатели на указатели, работающие в FFI схемы Gambit-C

Я нахожусь в процессе обёртывания API моего графического движка в Gambit-C и до сих пор добился успеха в FFI. Сегодня я столкнулся с новой проблемой, которую я не могу легко преодолеть.

У меня есть такая структура в C:

typedef struct render_list_rec
{
    long render_id;
    render_node* node;
    struct render_list_rec* next; 
} render_list;

В Си у меня также есть ряд функций, которые определяются макросами для добавления общих поведений списка. Затем в конечном итоге выглядит примерно так:

void render_list_item_add(render_list_item **list, render_list_item* elem);

В C вы можете иметь render_list_item*, который имеет значение NULL, но может передавать его первому параметру этой функции, и он по существу создаст для вас заголовок списка.

Моя проблема в том, что я не могу заставить это поведение работать в FFI Gambit-C. В итоге я создаю что-то вроде этого:

(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"))

Когда я запускаю это, это segfaults. После расследования ___arg1 процедуры render-list-add-item имеет значение 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>)

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

РЕДАКТИРОВАТЬ:

Феликс: Можете ли вы привести несколько примеров того, как вы вызываете 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))

Надежда была на подобное поведение. Из документации видно, что наличие #f в C API переводит в NULL, но я подумал (указатель (указатель)) мог бы это поймать. Даже передача переменных, привязанных к чему-либо, всегда приводила к значению NULL. Я проверил это, создав функцию в (c-Declare), которая просто печатает адрес указателя:

Если вы хотите увидеть мои полные обертки в действии, вы можете посмотреть здесь этот коммит

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

Вопрос о том, как заставить (указатель (указатель)) работать, все еще стоит. Но я думаю, что для более быстрых результатов и лучшей совместимости с другими языками, я собираюсь переписать свои макросы списка C, чтобы определить структуру списка, которая затем будет содержать указатели на начало / конец списка, как видно в "Мастеринг алгоритмов с C", Таким образом, указатель на указатели не понадобится.

1 ответ

Решение

Может быть, я неправильно понимаю, но когда у вас есть

(define ui-render-list #f)

тогда я думаю, что выражение:

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

будет вести себя как:

"взывать pg-render-list-item-add с фактическими аргументами #f и все model-data обозначает.

А затем FFI Gambit-C переводит значение схемы #f к значению C NULL (т.е. 0) когда он пересекает границу из Схемы в 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, но я думаю, что одним из способов достижения желаемого эффекта было бы выделение получающей памяти самостоятельно (путем подключения к malloc функции в FFI), а затем, как только вы выделите память, у вас будет адрес, который вы можете передать в качестве первого параметра pg-render-list-item,

Другие вопросы по тегам