Не удается получить указатели на указатели, работающие в 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
,