摘要:第一步打开项目下的文件,在文件中输入我们的函数的原型声明代码。这行代码注册一个原型为的函数,当这个函数被执行的时候,我们的函数将被运行时调用。原文地址开发扩展之原生函数定义
在上一篇中我们在hellozapi扩展中我们定义了几个常量,但是一个有用的扩展,必须得有函数,没有函数的扩展啥用没有,如果您觉得定义函数很难的话,您又错了,zendAPI就是为了让您生活变得美好而生的,而不会让事情变得复杂。
说到函数,咱们就不得不说函数最重要的两个组成部分,一个是函数的参数,另一个是函数的返回值。因为C++是静态语言,所以咱们的函数的类型必须在编译时就要确定,不像PHP语言中那么灵活。
zendAPI主要支持如下几种函数原型:
有返回值, 无参数
有返回值, 有参数
有返回值, 可变参数
无返回值, 无参数
无返回值, 有参数
无返回值, 可变参数
说明:zendAPI支持引用类型的参数传递
考虑到我们是新手学堂,在本篇中我们就不介绍可变参数和引用传参了,这部分我们放在我们的高级教程部分讲。
我们会在hellozapi中定义以下PHP原型的函数:(PHP 语言描述)
print_project_name($prefix);
print_develop_team();
get_version();
add_two_num($num1, $num2);
下面我们声明这几个PHP函数对应的C++函数原型
C++ Codeusing zapi::ds::Variant; using zapi::ds::NumericVaraint; using zapi::ds::StringVariant; void print_project_name(const StringVariant &prefix); void print_develop_team(); Variant get_version(); Variant add_two_num(const NumericVariant &num1, const NumericVariant num2);背景知识学习
在上面的C++函数的原型声明中出现两个陌生的类Variant和NumericVariant, 不要担心,现在我们简单介绍一下这两个类。
在zendAPI中,zapi::ds::Variant类的一个对象就代表PHP的一个变量,您可以将zapi::ds::Variant想象成一个容器,它将常见的C++类型包装成一个zapi::ds::Variant对象,方便跟zend engine整合。
您可以用这个类去包装如下类型:
常见的整形 (int, std::int8_t, std::int16_t, std::int32_t, long ... )
浮点型 (float, double)
布尔型 (true, false)
字符串 (std::string, char *, char [])
空指针 (std::nullptr_t)
上面说的既然zapi::ds::Variant可以包装一切必要的类型,是不是就够了呢?答案是否定的,虽然zapi::ds::Variant可以容纳C++的这些数据类型,但是它不提供任何特定类型的计算,比如常见的四则运算,字符串连接,函数调用等等。
那么问题又来了,你可能会问,为什么不提供这样的接口呢?接下来我就来解释下为什么不在zapi::ds::Variant为什么不提供这些接口,原因有如下几点:
1.zapi::ds::Variant设计的目的就是充当一个容器,方便zendAPI向zend engine进行数据传递,它强调的数据传递而不是数据的计算。
2.zendAPI的设计理念是,单一的类完成单一的任务,把字符串操作和整形操作甚至函数调用等等杂在一起违背了这个理念。
using zapi::ds::Variant; Varaint nullVar(nullptr); Variant numVar(123); Variant doubleVar(3.14);
根据上面讨论的,看着名字不用我说,大家都能猜出这个类的作用吧,没错,您猜的是对的,这个是对zapi::ds::Variant再次封装,为数值类型的zapi::ds::Variant提供数值计算的能力,比如四则运算, 大小比较运算。
using zapi::ds::NumericVariant; NumericVariant num1(123); NumericVariant num2(321); NumericVariant sum = num1 + num2; long rawSum = sum.toLong(); bool cmp = num1 < num2; // cmp is true std::int32_t raw32int1 = 123; std::int16_t raw32int2 = 23; NumericVariant num3(raw32int1); // value is 123 NumericVariant num4(raw32int2); // value is 23 sum = num3 + num4; // sum is 146
zapi::ds::NumericVariant 参考手册
这个类跟zapi::ds::NumericVariant一样,看名字我们就知道这个类是为字符串操作而设计的,它为我们提供了常见的字符串接口,拼接,子串查找,替换等等。下面我们就举几个常见的使用的范例:
using zapi::ds::StringVariant; StringVariant str1("hello zapi"); // str1 is hello zapi str1 += ", hello"; // now hello zapi, hello char c = str1[0]; // c is h std::string upperStr1 = str1.toUpperCase(); str1.replace("zapi", "zendAPI"); // str1 is hello zendAPI, hello str1.prepend("=> "); // str1 now is => hello zendAPI, hello
zapi::ds::StringVariant 参考手册
好了数据类型了解完毕,我们下面开始进入实现环节。
第一步打开hellozapi项目下的hellozapi/defs.h文件,在文件中输入我们的C++函数的原型声明代码。
#ifndef ZAPI_HELLOZAPI_DEFS_H #define ZAPI_HELLOZAPI_DEFS_H #include "zapi/ZendApi.h" using zapi::ds::Variant; using zapi::ds::NumericVariant; using zapi::ds::StringVariant; void print_project_name(const StringVariant &prefix); void print_develop_team(); Variant get_version(); Variant add_two_num(const NumericVariant &num1, const NumericVariant &num2); #endif // ZAPI_HELLOZAPI_DEFS_H第二步
打开hellozapi项目下的hellozapi/impls.cpp文件,在文件中输入我们的C++函数的实现代码。
#include "defs.h" #include第三步void print_project_name(const StringVariant &prefix) { zapi::out << prefix << " " << "hellozapi" << std::endl; } void print_develop_team() { zapi::out << "qcoreteam" << std::endl; } Variant get_version() { return "v1.0.2"; } Variant add_two_num(const NumericVariant &num1, const NumericVariant &num2) { return num1 + num2; }
将我们的实现的C++函数与zend engine进行整合。打开我们的入口文件hellozapi/entry.cpp,输入我们的函数注册代码。
#include "zapi/ZendApi.h" #include "defs.h" using zapi::lang::Constant; using zapi::lang::ValueArgument; extern "C" { ZAPI_DECL_EXPORT void *get_module() { static zapi::lang::Extension hellozapi("hellozapi", "1.0"); Constant hellozapiVersionConst("HELLO_ZAPI_VERSION", 0x010002); Constant hellozapiNameConst("HELLO_ZAPI_NAME", "Hello zendAPI!"); Constant helloDebugModeConst("HELLO_DEBUG_MODE", true); Constant helloPiConst("HELLO_ZAPI_PI", 3.14); hellozapi.registerConstant(std::move(hellozapiVersionConst)); hellozapi.registerConstant(std::move(hellozapiNameConst)); hellozapi.registerConstant(std::move(helloDebugModeConst)); hellozapi.registerConstant(std::move(helloPiConst)); hellozapi.registerFunction("print_project_name", { ValueArgument("prefix", zapi::lang::Type::String) }); hellozapi.registerFunction ("print_develop_team"); hellozapi.registerFunction ("get_version"); hellozapi.registerFunction ("add_two_num", { ValueArgument("num1", zapi::lang::Type::Numeric), ValueArgument("num2", zapi::lang::Type::Numeric) }); return hellozapi; } }
到这里,代码稍稍有些复杂了,但是细心的同学会发现,其实代码是很有规律的,只是重复调用而已,在这段代码中我们引入了几个新的类型,下面我先将这样类型做些讲解,然后我们再对这个代码段进行解释。
zendAPI对zend engine的宏类型定义重新用enum class进行了重新定义,方便实施C++的类型检查,比如常用的类型有:
zapi::lang::Type::Undefined
zapi::lang::Type::Null
zapi::lang::Type::False
zapi::lang::Type::True
zapi::lang::Type::Long
zapi::lang::Type::String
zapi::lang::Type 参考手册
zendAPI支持的参数传递有两种,按值传参和按引用传参。zapi::lang::ValueArgument类型就是为了支持按值传递参数机制,它的构造函数很简单,第一个参数是传递的参数的名字,第二个参数是这个参数的类型,第三个参数设置这个参数是否是必须的参数。
比如下面的代码我们定义了一个名叫arg1的参数,类型是字符串并且是非必要参数
ValueArgument("arg1", zapi::lang::Type::String, false);
zapi::lang::ValueArgument 参考手册
为了支持不同类型的函数,zapi::lang::Extension::registerFunction被设计成了一个模板函数,在这篇文章中我们暂时使用了用于注册非成员函数指针的部分。
传递的模板参数有:
函数的类型 (一般我们不定义函数的类型,使用decltype进行获取)
函数指针值 (会被zendAPI在运行时进行调用)
decltype 参考手册
传递的调用参数有:
函数的名字
函数接受的参数列表std::initializer_list
这里的 zapi::lang::Argument 是 zapi::lang::ValueArgument 的基类,一般不直接使用。
zapi::lang::Extension::registerFunction 参考手册
std::initializer_list 参考手册
有了上面的背景知识,现在我们解释函数注册代码就简单多了,您也很容易就能理解。
hellozapi.registerFunction("print_project_name", { ValueArgument("prefix", zapi::lang::Type::String) });
这行代码注册一个原型为print_project_name($prefix);的PHP函数,当这个函数被zend engine执行的时候,我们的C++函数void print_project_name(const StringVariant &prefix);将被运行时调用。
hellozapi.registerFunction("print_develop_team");
这行代码注册一个原型为print_develop_team的PHP函数,当这个函数被zend engine执行的时候,我们的C++函数void print_develop_team();将被运行时调用。
hellozapi.registerFunction("get_version");
这行代码注册一个原型为get_version的PHP函数,当这个函数被zend engine执行的时候,我们的C++函数Variant get_version();将被运行时调用。
hellozapi.registerFunction("add_two_num", { ValueArgument("num1", zapi::lang::Type::Numeric), ValueArgument("num2", zapi::lang::Type::Numeric) });
这行代码注册一个原型为add_two_num的PHP函数,当这个函数被zend engine执行的时候,我们的C++函数Variant add_two_num(const NumericVariant &num1, const NumericVariant &num2);将被运行时调用。
我们走到这里,函数注册就完成了,虽然有些小长,但是您不也坚持看完了吗?
下面让我们在PHP代码中愉快的进行调用吧。
怎么样,实现函数也不过如此吧,根本没啥难度,哈哈哈,您到时候也能自豪的说,我也能没事的试试写写扩展啦,给PHP语言添加几个原生函数了。下一篇,我们来点更刺激的,教大家怎么实现原生的Class。
原文地址:C++ 开发 PHP 7 扩展之原生函数定义
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/30635.html
摘要:大家如果经常阅读官方手册的话会发现,在扩展那一章里面的每个扩展的介绍的时候,都有一节是预定义常量,这些常量是不需要您在里面进行定义就可以使用的。比如扩展的那么我们必须也在我们扩展中也定义几个常量玩玩啊,其实真的很简单,不信那咱们走着看。 大家如果经常阅读 PHP 官方手册的话会发现,在扩展那一章里面的每个扩展的介绍的时候,都有一节是 Predefined Constants 预定义常量...
摘要:每一个扩展必须有一个描述对象,在中我们类主要的作用主要完成这个功能。表示我们扩展导出符号给其他库使用。文章使用的编程文档的引用连接参考手册参考手册原文链接开发扩展之模块入口定义 zendAPI 项目不提供任何底层的功能,只是封装了 zend engine 提供的功能,对上提供一个易用的编程接口。这篇文章中,我们将介绍 C++ 世界与 C 世界交汇的地方,在这里也是 zendAPI 的接...
摘要:比如扩展的那么我们必须也在我们扩展中也定义几个常量玩玩啊,其实真的很简单,不信那咱们走着看。好了,到这里我们就把预定义常量就讲完了,我没有骗您吧,真的很简单,稍作调整让我们继续前进原文链接开发扩展之定义常量 大家如果经常阅读 PHP 官方手册的话会发现,在扩展那一章里面的每个扩展的介绍的时候,都有一节是 Predefined Constants 预定义常量,这些常量是不需要您在 PHP...
摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...
摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...
阅读 2366·2021-11-22 14:56
阅读 1174·2019-08-30 15:55
阅读 3205·2019-08-29 13:29
阅读 1353·2019-08-26 13:56
阅读 3483·2019-08-26 13:37
阅读 558·2019-08-26 13:33
阅读 3348·2019-08-26 13:33
阅读 2228·2019-08-26 13:33