08 Лямбда-функции.
C ++ и PHP поддерживают лямбда-функции или анонимные функции (в мире C ++ наиболее часто используется слово лямбда, люди PHP говорят об «анонимных функциях»). С PHP-CPP вы можете передавать эти функции с одного языка на другой. Вы можете вызвать анонимную функцию PHP из вашего кода на C ++, а наоборот - вызвать lambda из C ++ из PHP-скрипта.
Вызов анонимных функций PHP из C ++
Начнем с очень простого примера в PHP. В PHP вы можете создавать анонимные функции и назначать их переменной (или передавать их непосредственно функции).
<?php
// анонимная функция PHP, хранящаяся в переменной $f
$f = function($a, $b) {
// вернуть сумму параметров
return $a + $b;
};
// передать функцию другой функции
other_function($f);
// или передать анонимную функцию, не присваивая ее переменной
other_function(function() {
// вернуть произведение параметров
return $a * $b;
});
?>
Вышеприведенный код должен быть знаком большинству PHP-программистов. Конечно, «other_function» может быть реализована в пространстве пользователя PHP, но чтобы продемонстрировать, как это сделать с PHP-CPP, мы собираемся построить его с помощью C ++. Подобно всем другим функциям, которые вы видели в предыдущих примерах, такая функция функций C ++ получает в качестве своего параметра объект Php::Parameters, который является std::vector объектов Php::Value.
#include <phpcpp.h>
/**
* Собственная функция, вызываемая из PHP
*
* Эта функция получает один параметр, содержащий вызываемую анонимную
* функцию PHP.
*
* @param params Параметры, передаваемые функции
*/
void other_function(Php::Parameters ¶ms)
{
// убедиться, что функция действительно вызывалась хотя бы с одним параметром
if (params.size() == 0) return nullptr;
// эта функция вызывается из пользовательского пространства PHP и вызывается
// с анонимной функцией в качестве первого параметра
Php::Value func = params[0];
// класс Php:: Value реализовал оператор (), который позволяет нам
// использовать объект так же, как если бы это была реальная функция
Php::Value result = func(3, 4);
// @todo сделать что-нибудь с результатом
}
/**
* Переключиться на контекст C, потому что механизм Zend ожидает,
* что get_module) будет иметь сигнатуру функции стиля C
*/
extern "C" {
/**
* Функция запуска, которая автоматически вызывается механизмом Zend
* при запуске PHP, и которая должна возвращать детали расширения
* @return void*
*/
PHPCPP_EXPORT void *get_module()
{
// объект расширения
static Php::Extension extension("my_extension", "1.0");
// добавить функцию примера, чтобы ее можно было вызвать из PHP-скриптов
extension.add<other_function>("other_function");
// вернуть детали расширения
return extension;
}
}
Это так просто. Но и наоборот. Представьте, что у нас есть функция в PHP-пространстве кода пользователя, которая принимает функцию обратного вызова. Следующая функция представляет собой простую версию функции PHP array_map ():
<?php
// функция, которая выполняет итерацию по массиву и вызывает функцию
// для каждого элемента этого массива, возвращает новый массив
// с каждым элементом, замененным результатом обратного вызова
function my_array_map($array, $callback) {
// переменная начального результата
$result = array();
// цикл по массиву
foreach ($array as $index => $item) {
// вызов обратного вызова по элементу
$result[$index] = $callback($item);
}
// готово
return $result;
}
?>
Представьте, что мы хотим называть эту функцию PHP из вашего кода на C++, используя функцию лямбда C++ в качестве обратного вызова. Это возможно и легко:
#include <phpcpp.h>
/**
* Собственная функция, вызываемая из PHP
*/
void run_test()
{
// создание анонимной функции
Php::Function multiply_by_two([](Php::Parameters ¶ms) -> Php::Value {
// убедитесь, что функция действительно вызывалась хотя бы с одним параметром
if (params.empty()) return nullptr;
// в функцию передается один параметр
Php::Value param = params[0];
// умножьте параметр на два
return param * 2;
});
// функция теперь вызывается
Php::Value four = multiply_by_two(2);
// объект Php::Function - это производное значение Php::Value, и его значение
// также может быть сохранено в обычном объекте Php::Value,
// тогда он все равно будет функцией обратного вызова, тогда
Php::Value value = multiply_by_two;
// объект значения теперь также содержит функцию
Php::Value six = value(3);
// create an array
Php::Value array;
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
// вызвать функцию пользовательского пространства
Php::Value result = Php::call("my_array_map", array, multiply_by_two);
// @todo что-то делать с переменной результата (которая теперь
// содержит массив со значениями 2, 4, 6 и 8).
}
/**
* Переключиться на C-контекст, потому что движок Zend ожидает,
* что get_module () будет иметь подпись функции стиля C
*/
extern "C" {
/**
* Функция запуска, которая автоматически вызывается механизмом Zend
* при запуске PHP, и которая должна возвращать детали расширения
* @return void*
*/
PHPCPP_EXPORT void *get_module()
{
// объект расширения
static Php::Extension extension("my_extension", "1.0");
// добавить функцию примера, чтобы ее можно было вызвать из PHP-скриптов
extension.add<run_test>("run_test");
// вернуть детали расширения
return extension;
}
}
В примере мы назначили функцию лямбда C++ объекту Php::Function. Класс Php::Function получен из класса Php::Value. Единственная разница между Php::Value и Php::Function заключается в том, что конструктор Php::Function принимает функцию. Несмотря на эту разницу, оба класса полностью идентичны. На самом деле мы предпочли бы, чтобы функции C++ напрямую привязывались к объектам Php::Value и пропускали конструктор Php::Function, но это невозможно из-за вызова неоднозначностей.
Класс Php::Function можно использовать так, как если бы это был обычный объект Php::Value: вы можете назначить его другим объектам Php::Value, и вы можете использовать его в качестве параметра при вызове функций PHP в пространстве пользователя. В приведенном выше примере мы делаем именно это: мы вызываем функцию my_iterate () пользовательского пространства с помощью нашей собственной функции 'multiply_by_two' C ++.
Подпись функции C ++
Вы можете передавать различные типы функций C++ в конструктор Php::Function, если они совместимы со следующими двумя сигнатурами функций:
Php::Value function(); Php::Value function(Php::Parameters ¶ms);
Внутри класса Php::Function используется объект std::function C ++ для хранения функции, поэтому все, что может быть сохранено в таком объекте std :: function, может быть присвоено классу Php::Function.