Это команда perlcall, которую можно запустить в бесплатном хостинг-провайдере OnWorks, используя одну из наших многочисленных бесплатных онлайн-рабочих станций, таких как Ubuntu Online, Fedora Online, онлайн-эмулятор Windows или онлайн-эмулятор MAC OS.
ПРОГРАММА:
ИМЯ
perlcall - Соглашения о вызовах Perl из C
ОПИСАНИЕ
Цель этого документа - показать вам, как вызывать подпрограммы Perl непосредственно из C,
т.е. как писать обратные вызовы.
Помимо обсуждения интерфейса C, предоставляемого Perl для написания обратных вызовов, документ
использует серию примеров, чтобы показать, как интерфейс действительно работает на практике. В
Кроме того, рассматриваются некоторые методы кодирования обратных вызовов.
Примеры, в которых необходимы обратные вызовы, включают
· Обработчик ошибок
Вы создали интерфейс XSUB для C API приложения.
Довольно распространенная функция в приложениях - это возможность определить функцию C, которая
будет вызываться всякий раз, когда происходит что-то неприятное. Мы бы хотели иметь возможность
укажите подпрограмму Perl, которая будет вызываться вместо этого.
· Программа, ориентированная на события
Классический пример использования обратных вызовов - это запись события, управляемого событиями.
программа, например, для приложения X11. В этом случае вы регистрируете функции как
вызывается всякий раз, когда происходят определенные события, например, нажата кнопка мыши, курсор
перемещается в окно или выбирается пункт меню.
Хотя описанные здесь методы применимы при встраивании Perl в программу на языке C,
это не основная цель этого документа. Есть и другие детали, которые должны быть
рассматриваются и относятся к встраиванию Perl. Подробнее о встраивании Perl в C см.
запутался.
Прежде чем приступить к изучению остальной части этого документа, было бы неплохо
Идея прочитать следующие два документа - perlxs и perlguts.
ВЫЗОВ_ ФУНКЦИИ
Хотя это легче объяснить на примерах, сначала вам нужно знать несколько
важные определения.
Perl имеет ряд функций C, которые позволяют вызывать подпрограммы Perl. Они есть
I32 call_sv (флаги SV * sv, I32);
I32 call_pv (char * subname, флаги I32);
I32 call_method (char * methname, флаги I32);
I32 call_argv (char * subname, I32 flags, char ** argv);
Ключевая функция: call_sv. Все остальные функции представляют собой довольно простые обертки, которые
упростить вызов подпрограмм Perl в особых случаях. В конце дня они будут
все звонят call_sv для вызова подпрограммы Perl.
Все вызов_* функции имеют параметр "flags", который используется для передачи битовой маски
варианты для Perl. Эта битовая маска действует идентично для каждой из функций. В
настройки, доступные в битовой маске, обсуждаются в разделе «ЗНАЧЕНИЯ ФЛАГА».
Теперь мы обсудим каждую из функций по очереди.
call_sv
call_sv принимает два параметра. Первый, «sv», - это SV *. Это позволяет указать
подпрограмму Perl, которая будет вызываться либо как строка C (которая сначала была преобразована
к SV) или ссылку на подпрограмму. Секция, . call_sv, показывает, как ты
может использовать call_sv.
call_pv
Функция, call_pv, похож на call_sv за исключением того, что он ожидает, что его первый параметр будет
быть символом C *, который определяет подпрограмму Perl, которую вы хотите вызвать, например,
"call_pv (" Фред ", 0)". Если подпрограмма, которую вы хотите вызвать, находится в другом пакете, просто
включите имя пакета в строку, например, «pkg :: fred».
вызов_метода
Функция вызов_метода используется для вызова метода из класса Perl. Параметр
«methname» соответствует имени вызываемого метода. Обратите внимание, что класс
метод, которому принадлежит метод, передается в стек Perl, а не в параметре
список. Этот класс может быть либо именем класса (для статического метода), либо
ссылка на объект (для виртуального метода). См. Perlobj для получения дополнительной информации о
статические и виртуальные методы и «Использование call_method» для примера использования
вызов_метода.
call_argv
call_argv вызывает подпрограмму Perl, указанную строкой C, хранящейся в "subname"
параметр. Также принимает обычный параметр «флаги». Последний параметр, "argv",
состоит из завершающегося NULL списка строк C, передаваемых в качестве параметров в
Подпрограмма Perl. Видеть . call_argv.
Все функции возвращают целое число. Это количество элементов, возвращенных
подпрограмма Perl. Фактические элементы, возвращаемые подпрограммой, хранятся на Perl.
стек.
Как правило, вам следует всегда проверьте возвращаемое значение из этих функций. Даже если
вы ожидаете, что Perl вернет только определенное количество значений.
подпрограмма, ничто не может помешать кому-то сделать что-то неожиданное - не говорите
вас не предупреждали.
ФЛАГ ЦЕННОСТИ:
Параметр «флаги» во всех вызов_* functions - одна из G_VOID, G_SCALAR или G_ARRAY,
которые указывают контекст вызова, объединенные оператором ИЛИ вместе с битовой маской любой комбинации
другие символы G_ *, определенные ниже.
G_VOID
Вызывает подпрограмму Perl в пустом контексте.
Этот флаг имеет 2 эффекта:
1. Он указывает вызываемой подпрограмме, что она выполняется в пустом контексте.
(если он выполняет хочу результатом будет неопределенное значение).
2. Это гарантирует, что на самом деле подпрограмма ничего не вернет.
Значение, возвращаемое вызов_* функция указывает, сколько элементов было возвращено
подпрограмма Perl - в этом случае это будет 0.
G_СКАЛАР
Вызывает подпрограмму Perl в скалярном контексте. Это настройка флага контекста по умолчанию
для всех вызов_* функции.
Этот флаг имеет 2 эффекта:
1. Он указывает вызываемой подпрограмме, что она выполняется в скалярном контексте.
(если он выполняет хочу результат будет ложным).
2. Это гарантирует, что на самом деле из подпрограммы возвращается только скаляр. В
подпрограмма, конечно, может игнорировать хочу и все равно вернуть список. Если так,
тогда будет возвращен только последний элемент списка.
Значение, возвращаемое вызов_* функция указывает, сколько элементов было возвращено
подпрограмма Perl - в этом случае это будет либо 0, либо 1.
Если 0, значит, вы указали флаг G_DISCARD.
Если 1, то элемент, фактически возвращенный подпрограммой Perl, будет сохранен в Perl.
стек - раздел Возврате a скаляр показывает, как получить доступ к этому значению в стеке.
Помните, что независимо от того, сколько элементов возвращает подпрограмма Perl, только последний
будет доступен из стека - подумайте о случае, когда только одно значение возвращается как
список только с одним элементом. Любые другие предметы, которые были возвращены, не будут существовать
контроль времени возвращается из вызов_* функция. Секция Возврате a список in a
скаляр контекст показывает пример такого поведения.
G_ARRAY
Вызывает подпрограмму Perl в контексте списка.
Как и в случае с G_SCALAR, этот флаг имеет 2 эффекта:
1. Он указывает вызываемой подпрограмме, что она выполняется в контексте списка.
(если он выполняет хочу результат будет правдой).
2. Это гарантирует, что все элементы, возвращенные из подпрограммы, будут доступны, когда
контроль возвращается из вызов_* функции.
Значение, возвращаемое вызов_* функция указывает, сколько элементов было возвращено
подпрограмма Perl.
Если 0, значит, вы указали флаг G_DISCARD.
Если не 0, то это будет подсчет количества элементов, возвращенных подпрограммой. Эти
элементы будут храниться в стеке Perl. Секция Возврате a список of ценности дает
пример использования флага G_ARRAY и механики доступа к возвращенным элементам из
стек Perl.
G_DISCARD
По умолчанию, вызов_* функции помещают элементы, возвращаемые подпрограммой Perl, на
стек. Если эти элементы вас не интересуют, то установка этого флажка сделает
Perl избавится от них автоматически. Обратите внимание, что по-прежнему можно указать
контекст подпрограммы Perl, используя либо G_SCALAR, либо G_ARRAY.
Если вы не установите этот флаг, то он очень важно убедиться, что любой
временные (т. е. параметры, переданные в подпрограмму Perl, и значения, возвращаемые из
подпрограммы) утилизируются самостоятельно. Секция Возврате a скаляр дает подробную информацию о том, как
для явного удаления этих временных файлов и раздела . Perl в распорядиться of
временные обсуждает конкретные обстоятельства, при которых вы можете игнорировать проблему и позволить
Perl сделает это за вас.
G_NOARGS
Всякий раз, когда подпрограмма Perl вызывается с использованием одного из вызов_* функции, предполагается
по умолчанию, параметры должны быть переданы подпрограмме. Если вы не проходите
параметры подпрограммы Perl, вы можете сэкономить немного времени, установив этот флаг. Это
приводит к тому, что массив @_ для подпрограммы Perl не создается.
Хотя функциональность, предоставляемая этим флагом, может показаться простой, она должна быть
используется только в том случае, если для этого есть веская причина. Причина осторожности в том, что даже
если вы указали флаг G_NOARGS, подпрограмма Perl все еще может
был вызван думать, что вы передали ему параметры.
На самом деле может случиться так, что вызванная вами подпрограмма Perl может получить доступ к @_
массив из предыдущей подпрограммы Perl. Это произойдет, когда исполняемый код
вызов_* Сама функция была вызвана из другой подпрограммы Perl. Код ниже
иллюстрирует это
суб Фред
{печать "@_ \ n"}
суб Джо
{& fred}
& Джо (1,2,3);
Это напечатает
1 2 3
Произошло то, что «fred» обращается к массиву @_, который принадлежит «joe».
G_EVAL
Вызываемая вами подпрограмма Perl может аварийно завершиться, например, из-за
требуют умереть явно или фактически не существующим. По умолчанию, когда любой из этих
происходит событие, процесс немедленно прекращается. Если вы хотите поймать этот тип
событие укажите флаг G_EVAL. Это поставит Eval { } вокруг вызова подпрограммы.
Когда управление возвращается из вызов_* функция, вам нужно проверить переменную $ @, когда вы
будет в обычном Perl-скрипте.
Значение, возвращаемое из вызов_* функция зависит от того, какие другие флаги были
указан и произошла ли ошибка. Вот все разные случаи, которые могут
происходят:
· Если вызов_* функция возвращается нормально, тогда возвращаемое значение соответствует указанному в
предыдущие разделы.
· Если указан G_DISCARD, возвращаемое значение всегда будет 0.
· Если указан G_ARRAY и произошла ошибка, возвращаемое значение всегда будет 0.
· Если указан G_SCALAR и произошла ошибка, возвращаемое значение будет 1 и
значение в верхней части стека будет недеф. Это означает, что если вы уже
обнаружил ошибку, проверив $ @, и вы хотите, чтобы программа продолжалась, вы должны
не забудьте открыть недеф из стека.
Увидеть . G_EVAL для получения подробной информации об использовании G_EVAL.
G_KEEPERR
Использование флага G_EVAL, описанного выше, всегда устанавливает $ @: очищает его, если не было
error и настройте его для описания ошибки, если в вызываемом коде была ошибка.
Это то, что вам нужно, если вы намерены обрабатывать возможные ошибки, но иногда вы
просто хочу перехватить ошибки и не дать им мешать работе остальной программы.
Этот сценарий в основном применим к коду, который должен вызываться изнутри.
деструкторы, асинхронные обратные вызовы и обработчики сигналов. В таких ситуациях, когда
вызываемый код имеет мало отношения к окружающему динамическому контексту, основная программа
необходимо изолировать от ошибок в вызываемом коде, даже если они не могут быть обработаны
разумно. Также может быть полезно сделать это с помощью кода для «__DIE__» или «__WARN__».
крючки и «связующие» функции.
Флаг G_KEEPERR предназначен для использования вместе с G_EVAL в вызов_* функции, которые
используются для реализации такого кода или с eval_sv. Этот флаг не влияет на
"call_ *" функционирует, когда G_EVAL не используется.
Когда используется G_KEEPERR, любая ошибка в вызываемом коде завершает вызов как обычно, и
ошибка не будет распространяться за пределы вызова (как обычно для G_EVAL), но и не будет
в $ @. Вместо этого ошибка будет преобразована в предупреждение с префиксом строки
"\ t (в очистке)". Это может быть отключено с помощью "разное" без предупреждений ". Если ошибки нет,
$ @ не будет очищен.
Обратите внимание, что флаг G_KEEPERR не распространяется на внутренние вычисления; они все еще могут установить $ @.
Флаг G_KEEPERR был представлен в Perl версии 5.002.
Увидеть . G_KEEPERR для примера ситуации, которая требует использования этого флага.
Определение Контекст
Как упоминалось выше, вы можете определить контекст выполняемой в данный момент подпрограммы в
Perl с хочу. Эквивалентный тест может быть выполнен на C с помощью макроса "GIMME_V",
который возвращает "G_ARRAY", если вы были вызваны в контексте списка, "G_SCALAR", если в
скалярный контекст или "G_VOID", если в пустом контексте (т. е. возвращаемое значение не будет
использовал). Более старая версия этого макроса называется «GIMME»; в пустом контексте он возвращает
«G_SCALAR» вместо «G_VOID». Пример использования макроса «GIMME_V» показан на
. . GIMME_V.
ПРИМЕРЫ
Хватит разговоров об определениях! Приведем несколько примеров.
Perl предоставляет множество макросов, помогающих получить доступ к стеку Perl. По возможности эти
Макросы всегда следует использовать при взаимодействии с внутренними компонентами Perl. Мы надеемся, что это должно сделать
код менее уязвим для любых изменений, внесенных в Perl в будущем.
Также стоит отметить, что в первой серии примеров я использовал только
call_pv функция. Это было сделано, чтобы упростить код и облегчить вам понимание
тема. По возможности, если выбор стоит между использованием call_pv и call_sv, вам следует
всегда стараюсь использовать call_sv. Посмотреть . call_sv для получения информации.
Нет Параметры, Ничего пока возвращенный
Этот первый тривиальный пример вызовет подпрограмму Perl, PrintUID, чтобы распечатать UID
процесс.
дополнительный PrintUID
{
print "UID равен $ <\ n";
}
и вот функция C для ее вызова
статическая пустота
call_PrintUID ()
{
ЦСП;
ПУШМАРК (SP);
call_pv ("PrintUID", G_DISCARD | G_NOARGS);
}
Все просто, а?
Несколько замечаний по поводу этого примера:
1. Игнорируйте пока "dSP" и "PUSHMARK (SP)". Они будут рассмотрены в следующем примере.
2. Мы не передаем никаких параметров в PrintUID поэтому можно указать G_NOARGS.
3. Нас не интересует ничего, что возвращается из PrintUID, поэтому указывается G_DISCARD.
Даже если PrintUID было изменено, чтобы возвращать некоторые значения, указав G_DISCARD, будет
означают, что они будут стерты к возвращению контроля времени из call_pv.
4. call_pv используется подпрограмма Perl, заданная как строка C. В этом
в случае, если имя подпрограммы было «жестко зашито» в код.
5. Поскольку мы указали G_DISCARD, нет необходимости проверять значение, возвращаемое из
call_pv. Всегда будет 0.
Прохождение параметры
Теперь давайте сделаем немного более сложный пример. На этот раз мы хотим назвать Perl
подпрограмма "LeftString", которая принимает 2 параметра - строку ($ s) и целое число ($ n).
Подпрограмма просто напечатает первые $ n символов строки.
Таким образом, подпрограмма Perl будет выглядеть так:
суб левая строка
{
мой ($ s, $ n) = @_;
напечатать substr ($ s, 0, $ n), "\ n";
}
Функция C, необходимая для вызова Левая строка будет выглядеть так:
статическая пустота
call_LeftString (а, б)
символ * а;
интервал б;
{
ЦСП;
ВХОДИТЬ;
СОХРАНИТЬTMPS;
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSVpv (a, 0)));
XPUSHs (sv_2mortal (newSViv (b)));
ВОЗВРАТ;
call_pv ("LeftString", G_DISCARD);
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
}
Вот несколько заметок о функции C call_LeftString.
1. Параметры передаются в подпрограмму Perl с помощью стека Perl. Это
назначение кода, начинающегося строкой «dSP» и заканчивающегося строкой «PUTBACK».
«DSP» объявляет локальную копию указателя стека. Эта локальная копия должна всегда
можно получить как «SP».
2. Если вы собираетесь поместить что-то в стек Perl, вам нужно знать, куда поместить
Это. Это цель макроса "dSP" - он объявляет и инициализирует локальным копия
указателя стека Perl.
Все остальные макросы, которые будут использоваться в этом примере, требуют, чтобы вы использовали этот
макро.
Исключением из этого правила является вызов подпрограммы Perl непосредственно из
Функция XSUB. В этом случае нет необходимости явно использовать макрос "dSP" - он
будет объявлен за вас автоматически.
3. Любые параметры, которые должны быть помещены в стек, должны быть заключены в квадратные скобки «КНОПКА» и
Макросы "PUTBACK". В данном контексте цель этих двух макросов - подсчитать
количество параметров, которые вы вводите автоматически. Затем всякий раз, когда Perl создает
массив @_ для подпрограммы, он знает, насколько он большой.
Макрос «PUSHMARK» сообщает Perl о необходимости запомнить текущий указатель стека.
Даже если вы не передаете никаких параметров (как в примере, показанном в разделе Нет
Параметры, Ничего пока возвращенный) вы все равно должны вызывать макрос "PUSHMARK", прежде чем сможете
позвонить любому из вызов_* функции - Perl все еще должен знать, что нет
параметры.
Макрос "PUTBACK" устанавливает глобальную копию указателя стека такой же, как и наша
локальная копия. Если бы мы этого не сделали, call_pv не знал бы, где два параметра, которые мы
нажаты были - помните, что до сих пор все манипуляции с указателем стека, которые мы выполняли
находится в нашей локальной копии, глобальная копия.
4. Далее мы переходим к XPUSH. Здесь параметры фактически передаются в
куча. В этом случае мы нажимаем строку и целое число.
См. «XSUB и стек аргументов» в perlguts для получения подробной информации о том, как макросы XPUSH
Работа.
5. Потому что мы создали временные ценности (с помощью sv_2mortal () звонки) нам придется
приведите в порядок стек Perl и избавьтесь от смертельных SV.
Это цель
ВХОДИТЬ;
СОХРАНИТЬTMPS;
в начале функции и
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
в конце. Пара "ENTER" / "SAVETMPS" создает границу для любых временных файлов, которые мы
Создайте. Это означает, что временные конструкции, от которых мы избавляемся, будут ограничены теми, от которых
были созданы после этих звонков.
Пара "FREETMPS" / "LEAVE" избавится от любых значений, возвращаемых Perl.
подпрограммы (см. следующий пример), плюс она также будет сбрасывать созданные нами смертельные SV.
Наличие "ENTER" / "SAVETMPS" в начале кода гарантирует, что никакие другие
смертные уничтожены.
Думайте об этих макросах как о работающих как "{" и "}" в Perl, чтобы ограничить объем
локальные переменные.
Смотрите раздел . Perl в избавиться of Временники для получения подробной информации об альтернативе
используя эти макросы.
6. В заключение, Левая строка теперь можно вызвать через call_pv функция. Единственный флаг
указанное на этот раз - G_DISCARD. Поскольку мы передаем Perl 2 параметра
подпрограмму на этот раз мы не указали G_NOARGS.
Возврате a скаляр
Теперь для примера работы с элементами, возвращаемыми из подпрограммы Perl.
Вот подпрограмма Perl, сумматор, который принимает 2 целочисленных параметра и просто возвращает их
сумма.
вспомогательный сумматор
{
мой ($ a, $ b) = @_;
$ a + $ b;
}
Поскольку теперь нас интересует возвращаемое значение из сумматор, функция C, необходимая для
назвать это теперь немного сложнее.
статическая пустота
call_Adder (а, б)
в а;
интервал б;
{
ЦСП;
счет;
ВХОДИТЬ;
СОХРАНИТЬTMPS;
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSViv (a)));
XPUSHs (sv_2mortal (newSViv (b)));
ВОЗВРАТ;
count = call_pv ("Сумматор", G_SCALAR);
ИСПАНИЯ;
если (количество! = 1)
квакать ("Большая проблема \ n");
printf ("Сумма% d и% d равна% d \ n", a, b, POPi);
ВОЗВРАТ;
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
}
На этот раз следует отметить следующие моменты:
1. Единственным указанным флагом на этот раз был G_SCALAR. Это означает, что массив @_ будет
создано и что значение, возвращаемое сумматор все еще будет существовать после вызова
call_pv.
2. Назначение макроса «SPAGAIN» - обновить локальную копию указателя стека.
Это необходимо, потому что возможно, что память, выделенная стеку Perl
был перераспределен во время call_pv вызов.
Если вы используете указатель стека Perl в своем коде, вы всегда должны обновлять
локальную копию с помощью SPAGAIN всякий раз, когда вы используете вызов_* функции или любые
другая внутренняя функция Perl.
3. Хотя ожидалось, что от сумматор, это все еще хорошо
практика проверять код возврата из call_pv так или иначе.
Ожидать единственного значения - не совсем то же самое, что знать, что оно будет. Если
кто-то модифицировал сумматор чтобы вернуть список, и мы не проверяли эту возможность и
предпримите соответствующие действия, стек Perl окажется в несогласованном состоянии. То есть
что-то ты на самом деле не хочу, чтобы когда-либо случалось.
4. Здесь используется макрос «POPi» для извлечения возвращаемого значения из стека. В этом случае
нам нужно целое число, поэтому мы использовали «POPi».
Вот полный список доступных макросов POP с типами, которые они возвращают.
СОЗ СВ
Указатель POPp
POPn двойной
Целое число POPi
POPl длинный
5. Последний "PUTBACK" используется, чтобы оставить стек Perl в согласованном состоянии перед
выход из функции. Это необходимо, потому что, когда мы извлекали возвращаемое значение из
стек с «POPi» обновил только нашу локальную копию указателя стека. Помнить,
«PUTBACK» устанавливает глобальный указатель стека таким же, как наша локальная копия.
Возврате a Список of Наши ценности
Теперь давайте расширим предыдущий пример, чтобы вернуть как сумму параметров, так и
разница.
Вот подпрограмма Perl
суб AddSubtract
{
мой ($ a, $ b) = @_;
($ a + $ b, $ a- $ b);
}
а это функция языка C
статическая пустота
call_AddSubtract (а, б)
в а;
интервал б;
{
ЦСП;
счет;
ВХОДИТЬ;
СОХРАНИТЬTMPS;
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSViv (a)));
XPUSHs (sv_2mortal (newSViv (b)));
ВОЗВРАТ;
count = call_pv ("ДобавитьСубтракт", G_ARRAY);
ИСПАНИЯ;
если (количество! = 2)
квакать ("Большая проблема \ n");
printf ("% d -% d =% d \ n", a, b, POPi);
printf ("% d +% d =% d \ n", a, b, POPi);
ВОЗВРАТ;
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
}
If call_AddSubtract называется так
call_AddSubtract (7, 4);
тогда вот результат
7 - 4 = 3
7 + 4 = 11
Заметки
1. Нам нужен контекст списка, поэтому мы использовали G_ARRAY.
2. Неудивительно, что в этот раз "POPi" используется дважды, потому что мы извлекали 2 значения.
из стека. Важно отметить, что при использовании макросов "POP *" они
слезть со стека в обратный порядка.
Возврате a Список in a скаляр Контекст
Скажем, подпрограмма Perl в предыдущем разделе была вызвана в скалярном контексте, например
статическая пустота
call_AddSubScalar (а, б)
в а;
интервал б;
{
ЦСП;
счет;
инт я;
ВХОДИТЬ;
СОХРАНИТЬTMPS;
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSViv (a)));
XPUSHs (sv_2mortal (newSViv (b)));
ВОЗВРАТ;
count = call_pv ("ДобавитьСубтракт", G_SCALAR);
ИСПАНИЯ;
printf ("Возвращено элементов =% d \ n", количество);
for (i = 1; i <= count; ++ i)
printf ("Значение% d =% d \ n", i, POPi);
ВОЗВРАТ;
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
}
Другая сделанная модификация заключается в том, что call_AddSubScalar напечатает количество элементов
возвращаются из подпрограммы Perl и их значение (для простоты предполагается, что они
целое число). Так что если call_AddSubScalar называется
call_AddSubScalar (7, 4);
тогда вывод будет
Возвращено товаров = 1
Значение 1 = 3
В этом случае главное отметить, что возвращается только последний элемент в списке.
из подпрограммы. ДобавитьВычесть на самом деле вернулся в call_AddSubScalar.
Возврате Данные от Perl с помощью Параметр Список
Также можно возвращать значения напрямую через список параметров - будь то
собственно желательно это сделать совсем другое дело.
Подпрограмма Perl, Inc, ниже принимает 2 параметра и напрямую увеличивает каждый.
суб Inc
{
++ $ _ [0];
++ $ _ [1];
}
и вот функция C для ее вызова.
статическая пустота
call_Inc (а, б)
в а;
интервал б;
{
ЦСП;
счет;
СВ * сва;
СВ*СВБ;
ВХОДИТЬ;
СОХРАНИТЬTMPS;
sva = sv_2mortal (новыйSViv (а));
svb = sv_2mortal (новыйSViv (б));
ПУШМАРК (SP);
XPUSHs (sva);
XPUSHs (SVB);
ВОЗВРАТ;
count = call_pv ("Inc", G_DISCARD);
если (количество! = 0)
croak ("call_Inc: ожидалось 0 значений от 'Inc', получено% d \ n",
считать);
printf ("% d + 1 =% d \ n", a, SvIV (sva));
printf ("% d + 1 =% d \ n", b, SvIV (svb));
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
}
Чтобы иметь возможность получить доступ к двум параметрам, которые были помещены в стек после их возврата
от call_pv необходимо записать их адреса - таким образом, две переменные
«сва» и «свб».
Причина, по которой это необходимо, заключается в том, что область стека Perl, в которой они находятся, будет очень
вероятно, были перезаписаны чем-то другим к моменту возврата контроля времени из call_pv.
. G_EVAL
Теперь пример с использованием G_EVAL. Ниже приведена подпрограмма Perl, которая вычисляет разницу между
его 2 параметра. Если это приведет к отрицательному результату, подпрограмма вызывает умереть.
sub вычесть
{
мой ($ a, $ b) = @_;
die "смерть может быть фатальной \ n", если $ a <$ b;
$ a - $ b;
}
и некоторые C называть это
статическая пустота
call_Subtract (а, б)
в а;
интервал б;
{
ЦСП;
счет;
ВХОДИТЬ;
СОХРАНИТЬTMPS;
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSViv (a)));
XPUSHs (sv_2mortal (newSViv (b)));
ВОЗВРАТ;
count = call_pv ("Вычесть", G_EVAL | G_SCALAR);
ИСПАНИЯ;
/ * Сначала проверяем eval * /
если (SvTRUE (ERRSV))
{
printf ("Ой -% s \ n", SvPV_nolen (ERRSV));
СОЗ;
}
еще
{
если (количество! = 1)
croak ("call_Subtract: хотел 1 значение из 'Subtract', получил% d \ n",
считать);
printf ("% d -% d =% d \ n", a, b, POPi);
}
ВОЗВРАТ;
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
}
If call_Subtract называется так
call_Subtract (4, 5)
следующее будет напечатано
Ой, смерть может быть фатальной
Заметки
1. Мы хотим уловить умереть поэтому мы использовали флаг G_EVAL. Без указания
этот флаг будет означать, что программа немедленно завершится в умереть
заявление в подпрограмме вычитать.
2. Код
если (SvTRUE (ERRSV))
{
printf ("Ой -% s \ n", SvPV_nolen (ERRSV));
СОЗ;
}
является прямым эквивалентом этой части Perl
напечатайте "Ой - $ @ \ n", если $ @;
"PL_errgv" - это глобальный Perl типа "GV *", указывающий на запись в таблице символов.
содержащий ошибку. Таким образом, «ERRSV» относится к C-эквиваленту $ @.
3. Обратите внимание, что стек извлекается с помощью «POP» в блоке, где «SvTRUE (ERRSV)»
правда. Это необходимо, потому что всякий раз, когда вызов_* функция вызывается с помощью
G_EVAL | G_SCALAR возвращает ошибку, вершина стека содержит значение недеф, Потому как
мы хотим, чтобы программа продолжалась после обнаружения этой ошибки, важно, чтобы
стек можно убрать, удалив недеф.
. G_KEEPERR
Рассмотрим этот довольно забавный пример, в котором мы использовали версию XS.
Пример call_Subtract внутри деструктора:
пакет Foo;
sub new {bless {}, $ _ [0]}
суб Вычесть {
мой ($ a, $ b) = @_;
die «смерть может быть фатальной», если $ a <$ b;
$ a - $ b;
}
sub DESTROY {call_Subtract (5, 4); }
sub foo {die "foo dies"; }
основной пакет;
{
мой $ foo = Foo-> новый;
eval {$ foo-> foo};
}
напечатайте "Saw: $ @" if $ @; # должно быть, но не
В этом примере не удастся распознать, что ошибка произошла внутри "eval {}". Вот
почему: код call_Subtract был выполнен, пока Perl очищал временные объекты, когда
выход из внешнего блока в фигурных скобках, и поскольку call_Subtract реализован с call_pv
используя флаг G_EVAL, он быстро сбрасывает $ @. Это приводит к выходу из строя самого внешнего
проверка на $ @, и тем самым отказ от ловушки ошибок.
Добавление флага G_KEEPERR, чтобы call_pv вызов в call_Subtract читает:
count = call_pv ("Вычесть", G_EVAL | G_SCALAR | G_KEEPERR);
сохранит ошибку и восстановит надежную обработку ошибок.
. call_sv
Во всех предыдущих примерах я «жестко зашифровал» имя подпрограммы Perl, которое будет
вызывается из C. Однако в большинстве случаев удобнее указать
имя подпрограммы Perl из сценария Perl.
Рассмотрим код Perl ниже
суб Фред
{
print "Здравствуйте \ n";
}
CallSubPV («Фред»);
Вот фрагмент XSUB, который определяет CallSubPV.
аннулировать
CallSubPV (имя)
название символа
КОД:
ПУШМАРК (SP);
call_pv (имя, G_DISCARD | G_NOARGS);
Это нормально. Дело в том, что подпрограмму Perl можно указать только как
строка, однако Perl допускает ссылки на подпрограммы и анонимные подпрограммы. Этот
это здесь call_sv Полезно.
Код ниже для КоллСубСВ идентично CallSubPV за исключением того, что параметр "имя"
теперь определяется как SV *, и мы используем call_sv вместо call_pv.
аннулировать
CallSubSV (имя)
SV * имя
КОД:
ПУШМАРК (SP);
call_sv (имя, G_DISCARD | G_NOARGS);
Потому что мы используем SV для звонка Фред можно использовать следующее:
CallSubSV ("Фред");
CallSubSV (\ & fred);
$ ref = \ & fred;
CallSubSV ($ ref);
CallSubSV (sub {print "Привет \ n"});
Как вы можете видеть, call_sv дает вам гораздо большую гибкость в том, как вы можете указать Perl
подпрограмма.
Обратите внимание: если необходимо сохранить SV («имя» в примере выше)
что соответствует подпрограмме Perl, так что ее можно будет использовать позже в программе, это
недостаточно просто хранить копию указателя на SV. Скажите, что приведенный выше код был похож на
это:
статический SV * RememberSub;
аннулировать
SaveSub1 (имя)
SV * имя
КОД:
RememberSub = имя;
аннулировать
CallSavedSub1 ()
КОД:
ПУШМАРК (SP);
call_sv (запомнитьSub, G_DISCARD | G_NOARGS);
Причина, по которой это неверно, заключается в том, что к тому времени, когда вы начнете использовать указатель "RememberSub" в
"CallSavedSub1", он может относиться или не относиться к подпрограмме Perl, которая была записана в
«SaveSub1». Это особенно актуально для следующих случаев:
SaveSub1 (\ & fred);
CallSavedSub1 ();
SaveSub1 (sub {print "Здравствуйте \ n"});
CallSavedSub1 ();
К тому времени, когда каждый из приведенных выше операторов "SaveSub1" будет выполнен, SV * s, который
соответствующие параметры больше не будут существовать. Ожидайте сообщения об ошибке от Perl of
форма
Невозможно использовать неопределенное значение в качестве ссылки на подпрограмму в ...
для каждой строки «CallSavedSub1».
Аналогично с этим кодом
$ ref = \ & fred;
SaveSub1 ($ ref);
$ ref = 47;
CallSavedSub1 ();
вы можете ожидать, что одно из этих сообщений (которое вы фактически получите, зависит от версии
используемого вами Perl)
Нет ссылки на КОД в ...
Неопределенная подпрограмма & main :: 47 вызвана ...
Переменная $ ref могла ссылаться на подпрограмму "fred" всякий раз, когда вызов
«SaveSub1» был создан, но к моменту вызова «CallSavedSub1» теперь он содержит номер
47. Поскольку мы сохранили только указатель на исходный SV в «SaveSub1», любые изменения в $ ref
будет отслеживаться указателем «RememberSub». Это означает, что всякий раз, когда "CallSavedSub1"
вызывается, он попытается выполнить код, на который ссылается SV *
"RememberSub". В этом случае, однако, теперь оно относится к целому числу 47, поэтому ожидайте, что Perl будет
громко жаловаться.
Похожая, но более тонкая проблема проиллюстрирована этим кодом:
$ ref = \ & fred;
SaveSub1 ($ ref);
$ ref = \ & joe;
CallSavedSub1 ();
На этот раз всякий раз, когда вызывается «CallSavedSub1», он выполняет подпрограмму Perl «joe»
(при условии, что он существует), а не "fred", как первоначально запрашивалось при вызове
"SaveSub1".
Чтобы обойти эти проблемы, необходимо взять полную копию SV. Код
ниже показан "SaveSub2", модифицированный для этого.
статический SV * keepSub = (SV *) NULL;
аннулировать
SaveSub2 (имя)
SV * имя
КОД:
/ * Сделаем копию обратного вызова * /
если (keepSub == (SV *) NULL)
/ * Первый раз, поэтому создаем новый SV * /
keepSub = newSVsv (имя);
еще
/ * Был здесь раньше, поэтому перезаписываем * /
SvSetSV (keepSub, имя);
аннулировать
CallSavedSub2 ()
КОД:
ПУШМАРК (SP);
call_sv (keepSub, G_DISCARD | G_NOARGS);
Чтобы избежать создания нового SV каждый раз, когда вызывается "SaveSub2", функция сначала проверяет,
посмотрите, вызывался ли он раньше. Если нет, то выделяется место для нового SV и
ссылка на подпрограмму Perl "имя" копируется в переменную "keepSub" в одном
операция с использованием "newSVsv". После этого, когда вызывается "SaveSub2", существующий SV,
"keepSub" перезаписывается новым значением с помощью "SvSetSV".
. call_argv
Вот подпрограмма Perl, которая печатает все переданные ей параметры.
суб Список печати
{
мой (@list) = @_;
foreach (@list) {печать "$ _ \ n"}
}
А вот пример call_argv который позвонит Список печати.
статический символ * слова [] = {"альфа", "бета", "гамма", "дельта", NULL};
статическая пустота
call_PrintList ()
{
ЦСП;
call_argv ("PrintList", G_DISCARD, слова);
}
Обратите внимание, что в этом случае нет необходимости вызывать «PUSHMARK». Это потому что
call_argv сделаю это для вас.
. вызов_метода
Рассмотрим следующий код Perl:
{
пакет Mine;
sub новый
{
мой ($ тип) = сдвиг;
благословить [@_]
}
дополнительный дисплей
{
мой ($ self, $ index) = @_;
print "$ index: $$ self [$ index] \ n";
}
дополнительный PrintID
{
мой ($ class) = @_;
print "Это Class $ class версии 1.0 \ n";
}
}
Он реализует очень простой класс для управления массивом. Помимо конструктора,
«новый», он объявляет методы, один статический и один виртуальный. Статический метод PrintID,
выводит просто имя класса и номер версии. Виртуальный метод "Дисплей",
выводит единственный элемент массива. Вот пример его использования на Perl.
$ a = Mine-> new ('красный', 'зеленый', 'синий');
$ a->Дисплей(1);
Мой-> PrintID;
напечатает
1: зеленый
Это Class Mine версии 1.0
Вызвать метод Perl из C довольно просто. Требуются следующие вещи:
· Ссылка на объект для виртуального метода или имя класса для статического
метод
· Название метода
· Любые другие параметры, специфичные для метода
Вот простой XSUB, который иллюстрирует механизм вызова как «PrintID», так и
"Display" методы из C.
аннулировать
call_Method (ссылка, метод, индекс)
SV * исх.
char * метод
внутренний индекс
КОД:
ПУШМАРК (SP);
XPUSH (ref);
XPUSHs (sv_2mortal (newSViv (индекс)));
ВОЗВРАТ;
call_method (метод, G_DISCARD);
аннулировать
call_PrintID (класс, метод)
char * класс
char * метод
КОД:
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSVpv (класс, 0)));
ВОЗВРАТ;
call_method (метод, G_DISCARD);
Таким образом, методы «PrintID» и «Display» могут быть вызваны следующим образом:
$ a = Mine-> new ('красный', 'зеленый', 'синий');
call_Method ($ a, 'Показать', 1);
call_PrintID ('Мой', 'PrintID');
Единственное, что следует отметить, это то, что как в статических, так и в виртуальных методах имя метода
не передается через стек - используется как первый параметр для вызов_метода.
. GIMME_V
Вот тривиальный XSUB, который печатает контекст, в котором он выполняется в данный момент.
аннулировать
PrintContext ()
КОД:
I32 дай мне = GIMME_V;
если (дай == G_VOID)
printf ("Контекст пуст \ n");
иначе если (дай == G_SCALAR)
printf ("Контекст скалярный \ n");
еще
printf ("Контекст - это массив \ n");
А вот и Perl для его тестирования.
Контекст печати;
$ a = PrintContext;
@a = Контекст Печати;
Результатом этого будет
Контекст пуст
Контекст скалярный
Контекст - это массив
. Perl в избавиться of Временники
В приведенных на сегодняшний день примерах любые временные объекты, созданные в обратном вызове (т. Е. Параметры
передано в стек в вызов_* функция или значения, возвращаемые через стек) были
освобождается одним из следующих способов:
· Указание флага G_DISCARD с помощью вызов_*
· Явное использование пары "ENTER" / "SAVETMPS" - "FREETMPS" / "LEAVE"
Есть еще один метод, который можно использовать, а именно позволить Perl делать это за вас автоматически.
всякий раз, когда он восстанавливает контроль после завершения обратного вызова. Это делается просто не
используя
ВХОДИТЬ;
СОХРАНИТЬTMPS;
...
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
последовательность в обратном вызове (а не, конечно, указание флага G_DISCARD).
Если вы собираетесь использовать этот метод, вы должны знать о возможной утечке памяти, которая
могут возникнуть при очень специфических обстоятельствах. Чтобы объяснить эти обстоятельства, вам необходимо
немного знать о потоке управления между Perl и процедурой обратного вызова.
Примеры, приведенные в начале документа (обработчик ошибок и управляемый событиями
программа) являются типичными для двух основных видов управления потоком, которые вы, вероятно,
столкнуться с обратными вызовами. Между ними есть очень важное различие, поэтому платите
внимание.
В первом примере обработчика ошибок поток управления может быть следующим. У вас есть
создал интерфейс для внешней библиотеки. Управление может достигать внешней библиотеки, например
этой
perl -> XSUB -> внешняя библиотека
Пока элемент управления находится в библиотеке, возникает состояние ошибки. Вы ранее настроили
Обратный вызов Perl для обработки этой ситуации, поэтому он будет выполнен. Как только обратный вызов
завершено, управление снова перейдет к Perl. Вот каким будет поток управления
как в той ситуации
perl -> XSUB -> внешняя библиотека
...
происходит ошибка
...
внешняя библиотека -> call_ * -> perl
|
perl <- XSUB <- внешняя библиотека <- call_ * <---- +
После обработки ошибки с помощью вызов_* завершено, управление возвращается к Perl подробнее
или менее сразу.
На диаграмме, чем дальше вы идете вправо, тем глубже вложена область видимости. Это только
когда управление вернется с perl в крайнем левом углу диаграммы, у вас будет
упал обратно в охватывающий объем, и любые временные детали, которые вы оставили торчать, будут
быть освобожденным.
Во втором примере программы, управляемой событиями, поток управления будет примерно таким:
perl -> XSUB -> обработчик событий
...
обработчик событий -> call_ * -> perl
|
обработчик событий <- call_ * <---- +
...
обработчик событий -> call_ * -> perl
|
обработчик событий <- call_ * <---- +
...
обработчик событий -> call_ * -> perl
|
обработчик событий <- call_ * <---- +
В этом случае поток управления может состоять только из повторяющейся последовательности.
обработчик событий -> call_ * -> perl
практически на всю продолжительность программы. Это означает, что контроль может никогда
вернитесь в крайнюю левую область видимости Perl.
Так в чем же большая проблема? Что ж, если вы ожидаете, что Perl уберет эти временные файлы
тебя, возможно, придется долго ждать. Чтобы Perl избавился от ваших временных файлов,
на каком-то этапе элемент управления должен вернуться в ограничивающую область видимости. В сценарии, управляемом событиями
этого может никогда не случиться. Это означает, что со временем ваша программа будет создавать больше
и другие временные, ни одна из которых никогда не будет освобождена. Поскольку каждая из этих временных
потребляет некоторую память, ваша программа в конечном итоге потребляет всю доступную память в вашем
система - капов!
Итак, вот нижняя строка - если вы уверены, что управление вернется к окружающему
Область видимости Perl довольно быстро после завершения вашего обратного вызова, тогда это не совсем
необходимо явно избавиться от любых созданных вами временных файлов. Имейте в виду, если вы
совершенно не уверены, что делать, уборка в любом случае не причиняет вреда.
Стратегии для Хранение Обратный звонок Контекст Информация
Потенциально одна из самых сложных проблем, которую нужно преодолеть при разработке интерфейса обратного вызова.
можно выяснить, как сохранить сопоставление между функцией обратного вызова C и Perl
эквивалент.
Чтобы понять, почему это может быть реальной проблемой, сначала рассмотрим, как настроен обратный вызов.
во всей среде C. Обычно C API предоставляет функцию для регистрации
Перезвоните. Это будет ожидать указатель на функцию в качестве одного из ее параметров. Ниже приводится
вызов гипотетической функции "register_fatal", которая регистрирует функцию C, чтобы получить
вызывается при возникновении фатальной ошибки.
register_fatal (cb1);
Единственный параметр cb1 - это указатель на функцию, поэтому вы должны определить cb1 в
ваш код, скажите что-нибудь вроде этого
статическая пустота
cb1 ()
{
printf ("Неустранимая ошибка \ n");
выход(1);
}
Теперь измените это, чтобы вместо этого вызывать подпрограмму Perl
статический SV * обратный вызов = (SV *) NULL;
статическая пустота
cb1 ()
{
ЦСП;
ПУШМАРК (SP);
/ * Вызов подпрограммы Perl для обработки обратного вызова * /
call_sv (обратный вызов, G_DISCARD);
}
аннулировать
register_fatal (fn)
СВ * фн
КОД:
/ * Помните подпрограмму Perl * /
если (обратный вызов == (SV *) NULL)
callback = newSVsv (fn);
еще
SvSetSV (обратный вызов, fn);
/ * регистрируем обратный вызов во внешней библиотеке * /
register_fatal (cb1);
где Perl-эквивалент "register_fatal" и регистрируемый им обратный вызов, "pcb1", могут
выглядите так
# Зарегистрируем суб pcb1
регистр_фатальный (\ & pcb1);
суб pcb1
{
die "Я умираю ... \ n";
}
Отображение между обратным вызовом C и эквивалентом Perl хранится в глобальном
переменная "обратный вызов".
Этого будет достаточно, если вам когда-либо понадобится зарегистрировать только один обратный вызов в любое время.
Примером может служить обработчик ошибок, подобный приведенному выше коду. Помните, однако,
повторные вызовы "register_fatal" заменят ранее зарегистрированный обратный вызов
работать с новым.
Скажем, например, вы хотите подключиться к библиотеке, которая позволяет асинхронный ввод-вывод файлов. В
В этом случае вы можете зарегистрировать обратный вызов после завершения операции чтения.
Чтобы быть полезным, мы хотим иметь возможность вызывать отдельные подпрограммы Perl для каждого файла, который
открыт. В его нынешнем виде приведенный выше пример обработчика ошибок не подходит, поскольку он
позволяет определять только один обратный вызов в любое время. Нам нужны средства
сохранение соответствия между открытым файлом и подпрограммой Perl, которую мы хотим вызвать
для этого файла.
Скажем, в библиотеке ввода-вывода есть функция asynch_read, которая связывает функцию C
"ProcessRead" с дескриптором файла "fh" - предполагается, что он также предоставил некоторую процедуру
, чтобы открыть файл и получить дескриптор файла.
asynch_read (fh, ProcessRead)
Это может ожидать, что C ПроцессЧитать функция этой формы
аннулировать
ProcessRead (fh, буфер)
внутр. ч;
char * буфер;
{
...
}
Чтобы предоставить Perl-интерфейс для этой библиотеки, нам нужно иметь возможность отображать между "fh"
параметр и подпрограмму Perl, которую мы хотим вызвать. Хеш - удобный механизм для
сохранение этого отображения. В приведенном ниже коде показана возможная реализация
статический HV * Mapping = (HV *) NULL;
аннулировать
asynch_read (fh, обратный вызов)
внутр. ч
SV * обратный звонок
КОД:
/ * Если хеша еще нет, создадим его * /
if (Отображение == (HV *) NULL)
Отображение = newHV ();
/ * Сохраняем отображение fh -> callback * /
hv_store (отображение, (char *) & fh, sizeof (fh), newSVsv (обратный вызов), 0);
/ * Зарегистрируйтесь в библиотеке C * /
asynch_read (fh, asynch_read_if);
и "asynch_read_if" может выглядеть так
статическая пустота
asynch_read_if (fh, буфер)
внутр. ч;
char * буфер;
{
ЦСП;
СВ ** св;
/ * Получить обратный вызов, связанный с fh * /
sv = hv_fetch (отображение, (char *) & fh, sizeof (fh), FALSE);
если (sv == (SV **) NULL)
croak ("Внутренняя ошибка ... \ n");
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSViv (fh)));
XPUSHs (sv_2mortal (newSVpv (буфер, 0)));
ВОЗВРАТ;
/ * Вызов подпрограммы Perl * /
call_sv (* sv, G_DISCARD);
}
Для полноты, вот "asynch_close". Это показывает, как удалить запись из
хэш «Отображение».
аннулировать
asynch_close (fh)
внутр. ч
КОД:
/ * Удаляем запись из хеша * /
(void) hv_delete (отображение, (char *) & fh, sizeof (fh), G_DISCARD);
/ * Теперь вызовем настоящий asynch_close * /
asynch_close (fh);
Итак, интерфейс Perl будет выглядеть так
дополнительный обратный вызов1
{
мой ($ handle, $ buffer) = @_;
}
# Зарегистрируем обратный вызов Perl
asynch_read ($ fh, \ & callback1);
asynch_close ($ fh);
Отображение между обратным вызовом C и Perl хранится в глобальном хэше Mapping this
время. Использование хеша имеет явное преимущество в том, что он позволяет неограниченное количество
обратные вызовы, которые необходимо зарегистрировать.
Что делать, если интерфейс, предоставляемый обратным вызовом C, не содержит параметра, который позволяет
дескриптор файла для отображения подпрограммы Perl? Скажем, в пакете асинхронного ввода-вывода
функция обратного вызова получает только параметр "буфер", подобный этому
аннулировать
ProcessRead (буфер)
char * буфер;
{
...
}
Без дескриптора файла нет прямого способа сопоставить обратный вызов C с
Подпрограмма Perl.
В этом случае возможный способ решения этой проблемы состоит в том, чтобы заранее определить серию функций C для
выступать в качестве интерфейса для Perl, таким образом
# определить MAX_CB 3
# определить NULL_HANDLE -1
typedef void (* FnMap) ();
структура MapStruct {
Функция FnMap;
СВ * PerlSub;
целая ручка;
};
static void fn1 ();
static void fn2 ();
static void fn3 ();
статическая структура MapStruct Map [MAX_CB] =
{
{fn1, NULL, NULL_HANDLE},
{fn2, NULL, NULL_HANDLE},
{fn3, NULL, NULL_HANDLE}
};
статическая пустота
Печатная плата (индекс, буфер)
внутренний индекс;
char * буфер;
{
ЦСП;
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSVpv (буфер, 0)));
ВОЗВРАТ;
/ * Вызов подпрограммы Perl * /
call_sv (Карта [индекс] .PerlSub, G_DISCARD);
}
статическая пустота
fn1 (буфер)
char * буфер;
{
Pcb (0, буфер);
}
статическая пустота
fn2 (буфер)
char * буфер;
{
Pcb (1, буфер);
}
статическая пустота
fn3 (буфер)
char * буфер;
{
Pcb (2, буфер);
}
аннулировать
array_asynch_read (fh, обратный вызов)
внутр. ч
SV * обратный звонок
КОД:
внутренний индекс;
интервал null_index = MAX_CB;
/ * Находим тот же дескриптор или пустую запись * /
for (индекс = 0; индекс <MAX_CB; ++ индекс)
{
if (Карта [индекс] .Handle == fh)
перерыва;
if (Карта [индекс] .Handle == NULL_HANDLE)
null_index = индекс;
}
если (индекс == MAX_CB && null_index == MAX_CB)
croak ("Слишком много зарегистрированных функций обратного вызова \ n");
если (индекс == MAX_CB)
индекс = нулевой_индекс;
/ * Сохраняем дескриптор файла * /
Карта [индекс] .Handle = fh;
/ * Помните подпрограмму Perl * /
if (Map [index] .PerlSub == (SV *) NULL)
Карта [индекс] .PerlSub = newSVsv (обратный вызов);
еще
SvSetSV (Карта [индекс] .PerlSub, обратный вызов);
asynch_read (fh, карта [индекс]. функция);
аннулировать
array_asynch_close (fh)
внутр. ч
КОД:
внутренний индекс;
/ * Находим дескриптор файла * /
for (индекс = 0; индекс <MAX_CB; ++ индекс)
if (Карта [индекс] .Handle == fh)
перерыва;
если (индекс == MAX_CB)
croak ("не удалось закрыть fh% d \ n", fh);
Карта [индекс] .Handle = NULL_HANDLE;
SvREFCNT_dec (Карта [индекс] .PerlSub);
Карта [индекс] .PerlSub = (SV *) NULL;
asynch_close (fh);
В этом случае функции «fn1», «fn2» и «fn3» используются для запоминания Perl.
вызываемая подпрограмма. Каждая из функций имеет отдельный зашитый индекс, который
используется в функции «Pcb» для доступа к массиву «Map» и фактически вызывает Perl
подпрограмма.
У этой техники есть очевидные недостатки.
Во-первых, код значительно сложнее, чем в предыдущем примере.
Во-вторых, существует жесткое ограничение (в данном случае 3) на количество обратных вызовов, которые могут
существуют одновременно. Единственный способ увеличить лимит - это изменить код, добавив
больше функций, а затем перекомпиляция. Тем не менее, пока количество функций
выбранный с некоторой осторожностью, это все еще работоспособное решение, а в некоторых случаях является единственным
доступен.
Подводя итог, вот несколько возможных методов, которые вы можете рассмотреть для хранения
отображение между C и обратным вызовом Perl
1. Игнорируйте проблему - разрешите только 1 обратный вызов
Для многих ситуаций, таких как взаимодействие с обработчиком ошибок, это может быть
вполне адекватное решение.
2. Создайте последовательность обратных вызовов - жестко привязанный лимит
Если по параметрам, переданным обратно из обратного вызова C, невозможно сказать, что
контекст есть, тогда вам может потребоваться создать последовательность интерфейса обратного вызова C
функции и хранить указатели на каждую в массиве.
3. Используйте параметр для сопоставления с обратным вызовом Perl.
Хэш - это идеальный механизм для хранения сопоставления между C и Perl.
Заместитель Стек Манипуляция
Хотя я использовал только макросы «POP *» для доступа к значениям, возвращаемым из Perl
подпрограмм, также можно обойти эти макросы и прочитать стек, используя "ST"
макрос (полное описание макроса "ST" см. в perlxs).
В большинстве случаев макроса «POP *» должно быть достаточно; основная проблема с ними в том, что
они заставляют вас последовательно обрабатывать возвращенные значения. Это может быть не самый
подходящий способ обработки значений в некоторых случаях. Мы хотим иметь доступ к
складывать в случайном порядке. Макрос "ST", используемый при кодировании XSUB, идеально подходит для этого.
цель.
Код ниже - это пример, приведенный в разделе Возврате a Список of Наши ценности перекодировано в
используйте "ST" вместо "POP *".
статическая пустота
call_AddSubtract2 (а, б)
в а;
интервал б;
{
ЦСП;
Топор I32;
счет;
ВХОДИТЬ;
СОХРАНИТЬTMPS;
ПУШМАРК (SP);
XPUSHs (sv_2mortal (newSViv (a)));
XPUSHs (sv_2mortal (newSViv (b)));
ВОЗВРАТ;
count = call_pv ("ДобавитьСубтракт", G_ARRAY);
ИСПАНИЯ;
SP - = количество;
топор = (SP - PL_stack_base) + 1;
если (количество! = 2)
квакать ("Большая проблема \ n");
printf ("% d +% d =% d \ n", a, b, SvIV (ST(0)));
printf ("% d -% d =% d \ n", a, b, SvIV (ST(1)));
ВОЗВРАТ;
БЕСПЛАТНО;
ОСТАВЛЯТЬ;
}
Заметки
1. Обратите внимание, что необходимо было определить переменную «ax». Это потому, что "СТ"
макрос ожидает, что он существует. Если бы мы были в XSUB, не было бы необходимости определять
«топор», как это у нас уже определено.
2. Код
ИСПАНИЯ;
SP - = количество;
топор = (SP - PL_stack_base) + 1;
устанавливает стек так, чтобы мы могли использовать макрос "ST".
3. В отличие от исходного кода этого примера, возвращаемые значения не доступны в
обратный порядок. Так ST(0) относится к первому значению, возвращаемому подпрограммой Perl.
а «ST (count-1)» относится к последнему.
Создающий и призвание an Anonymous подпрограмма in C
Как мы уже показали, call_sv можно использовать для вызова анонимной подпрограммы. Тем не мение,
В нашем примере показан сценарий Perl, вызывающий XSUB для выполнения этой операции. Давайте посмотрим
как это можно сделать внутри нашего кода на C:
...
SV * cvrv = eval_pv ("sub {print 'Вы не обнаружите, что я загромождаю какое-либо пространство имен!'}", ИСТИНА);
...
call_sv (cvrv, G_VOID | G_NOARGS);
"eval_pv" используется для компиляции анонимной подпрограммы, которая будет возвращать значение как
ну (подробнее о eval_pv в eval_pv в perlapi). Как только эта ссылка на код находится в
рука, это может быть смешано со всеми предыдущими примерами, которые мы показали.
ЛЕГКИЙ ОТЗЫВЫ
Иногда вам нужно повторно вызывать одну и ту же подпрограмму. Обычно это происходит с
функция, которая работает со списком значений, например, встроенная в Perl Сортировать(). Вы можете пройти
функция сравнения с Сортировать(), который затем будет вызываться для каждой пары значений, которые
нужно сравнивать. В первый() и уменьшения () функции из List :: Util следуют аналогичному
шаблону.
В этом случае можно ускорить процедуру (часто довольно существенно), используя
легкий API обратного вызова. Идея состоит в том, что контекст вызова должен быть только
создается и уничтожается один раз, а подпрограмму можно вызывать произвольно много раз между ними.
Обычно параметры передаются с использованием глобальных переменных (обычно $ _ для одного параметра или
$ a и $ b для двух параметров), а не через @_. (Можно использовать механизм @_
если вы знаете, что делаете, хотя для этого еще нет поддерживаемого API. Это также
по своей сути медленнее.)
Шаблон макросов выглядит следующим образом:
dMULTICALL; / * Объявление локальных переменных * /
I32 gimme = G_SCALAR; / * контекст вызова: G_SCALAR,
* G_ARRAY или G_VOID * /
PUSH_MULTICALL (резюме); / * Настраиваем контекст для вызова cv,
и соответствующим образом установите локальные переменные * /
/* петля */ {
/ * установить значение (я) ваших переменных параметров * /
MULTICALL; / * Совершаем фактический вызов * /
} / * конец цикла * /
POP_MULTICALL; / * Удаляем вызывающий контекст * /
Для некоторых конкретных примеров см. Реализацию первый() и уменьшения () Функции
из List :: Util 1.18. Там вы также найдете заголовочный файл, имитирующий многоканальный API.
в старых версиях perl.
Используйте perlcall онлайн с помощью сервисов onworks.net