Регистрация нескольких вложенных ссылок Perl для библиотеки C в XS

Оба perlcall (в разделе "Стратегии для хранение информации о контексте обратного вызова») и списки Extending and Embedding Perl (в разделе "Обратный вызов") 3 разных способа обработки вызовов подпрограмм Perl из XS/C:

  1. Немедленно: XS звонит
  2. Отложено: сохранение дополнительной ссылки как SV* на потом.
  3. Несколько: сохраните n подссылок на потом

В приведенном выше примере и подробностях для № 3 используется хэш в XS для связывания вложенной ссылки с конкретной функцией C, но они предопределяют фиксированное количество функций C, что неадекватно.

Я работаю над интерфейсом XS для библиотеки C, которая использует обратные вызовы/указатели функций с необязательными аргументами, например:

  blah(custom_type *o, void (*func) (void *data, int more_data), const void * data);

C blah в этой библиотеке в конечном итоге вызовет переданную ей функцию вместе с переданными данными.

Если возможно, я хотел бы сделать сопоставление 1-к-1 C API с Perl. например

  blah($o, \&func, $data);

В настоящее время у меня есть № 2 выше, но другой вызов blah() перезапишет сохраненный SV *.

Как бы я реализовал № 3 выше?


person Adam Flott    schedule 10.12.2009    source источник
comment
сохранить мою ссылку на код Perl... чтобы позвонить позже? Что вызывает более поздний вызов? Непонятно, с какой частью у вас проблемы. Что касается нескольких, у вас это работает для одного и вы хотите его расширить? Или это только часть целой, еще не решенной задачи?   -  person ysth    schedule 11.12.2009
comment
Я обновил вопрос, чтобы, надеюсь, сделать его более понятным   -  person Adam Flott    schedule 11.12.2009


Ответы (2)


Это решение, которое я придумал:

Большинство обратных вызовов в этой библиотеке C принимают предоставленный пользователем void * и передают его в качестве первого аргумента. Поэтому я сохраняю SV * и предоставленные пользователем данные в структуре:

typedef struct __saved_callback {
    SV   *func;
    void *data;
} _saved_callback;

Моя функция XS выделит структуру _saved_callback и передаст ее в качестве первого аргумента call_perl_sub() с дополнительной ссылкой на Perl и предполагаемыми данными пользователя.

void
blah(obj, func, data)
    whatever *obj
    void *func
    void *data
    CODE:
        _saved_callback *sc = NULL;
        Newx(sc, 1, _saved_callback);
        sc->func = (SV *)func;
        sc->data = data;
        blah(obj, call_perl_sub, sc);

Затем вызовите подссылку Perl (я пропустил манипуляции со стеком для предоставленного пользователем аргумента данных):

void call_perl_sub(void *data) {
    dSP;
    int count;
    _saved_callback *perl_saved_cb = data;

    count = call_sv(perl_saved_cb->func, G_DISCARD);
    if ( count != 0 )
        croak("Expected 0 value got %d\n", count);
}
person Adam Flott    schedule 12.12.2009
comment
Примечание. Я уверен, что есть лучшее решение, но я все еще изучаю XS. - person Adam Flott; 12.12.2009

Я не могу дать вам полный ответ, к сожалению. Вместо этого позвольте мне дать вам несколько советов:

  • Вы не можете сопоставить ссылку на код Perl (технически CV*) с функцией C. Ссылки на код Perl на самом деле представляют собой данные (дерево OP) с некоторыми прикрепленными указателями функций для выполнения определенных битов работы в каждом OP. (См. проиллюстрированные внутренности perl для получения подробной информации о структурах OP).

  • Что вам может сойти с рук, так это написать свою собственную функцию C с необходимой подписью, передать ее бла C-уровня. Затем в коде XS получите обратные вызовы Perl (которые передаются как скаляр Perl (SV)). См. «perldoc perlapi». Найдите svtype и биты SVt_*.) Где-нибудь сохраните эти SV. Затем, когда вы хотите, чтобы библиотека C выполняла обратные вызовы, ваш обратный вызов C получает управление и может отправлять ваши обратные вызовы уровня Perl. См. "perldoc perlcall".

Удачи. Ты собираешься делать странные вещи.

person tsee    schedule 11.12.2009
comment
Кроме того, позвольте мне добавить, что мой ответ относится к тому, что, как я думаю, вы пытаетесь сделать. Я не на 100% уверен, что правильно понял ваши цели. (См. комментарий ysth к вашему вопросу.) - person tsee; 11.12.2009
comment
tsee: CV уровня perl хранятся как opttrees, CV уровня c — нет. perlcall перечисляет различные стратегии обратного вызова c для perl, и многие основные функции также используют их. например sort вызывает perl subs с varargs из c. Решение Адама Флотта является общей стратегией, но я его тщательно не проверял. - person rurban; 01.02.2011
comment
rurban: Интересно, что вы указываете на это. Прошло около года с тех пор, как я написал этот ответ, и с тех пор я многое узнал о внутренностях. Любопытно прочитать этот мой старый полуответ :) - person tsee; 02.02.2011