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

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

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 процедуры добавление-элемента-списка-рендеринга имеет значение 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». . Таким образом, указатель на указатели не понадобится.


person tbogdala    schedule 02.03.2013    source источник
comment
Можете ли вы привести несколько примеров того, как вы вызываете render-list-add-item? В частности, мне любопытно, как вы заранее настраиваете память, а затем берете ее адрес (аналогично оператору & в C)   -  person pnkfelix    schedule 03.03.2013
comment
Сделал редактирование вопроса, чтобы я мог ответить с прокомментированным кодом. К тому же я упирался в лимит символов.   -  person tbogdala    schedule 05.03.2013


Ответы (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.

person pnkfelix    schedule 05.03.2013
comment
Это хорошее объяснение проблемы. Я думаю, что выделение нового указателя в оболочке FFI не решит полностью мою проблему, поскольку я не вижу, как я могу вернуть его через API, если я не верну пару схем (‹возвращаемое значение›, ‹новый указатель списка›). Это лучший ответ на данный момент. - person tbogdala; 05.03.2013
comment
Вы можете держать в руках значение, возвращаемое malloc, когда вы его вызываете; это будет представлять собой начало списка с этого момента, насколько я понимаю ваш API. Это зависит от того, что вы хотите: собственная структура списка, невидимая для кода схемы (что malloc плюс ваш API может предоставить), или структура списка графического интерфейса, поддерживаемая списком схемы (что потребует больше искажений, если это вообще возможно). ). На Scheme Workshop 2008 были опубликованы документы по схеме FFI, см. сессию 3; они могут объяснить это больше. - person pnkfelix; 05.03.2013