摘要:第一步打開項目下的文件,在文件中輸入我們的函數的原型聲明代碼。這行代碼注冊一個原型為的函數,當這個函數被執行的時候,我們的函數將被運行時調用。原文地址開發擴展之原生函數定義
在上一篇中我們在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 擴展之原生函數定義
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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,相信小伙們一定有很多收獲...
閱讀 2367·2021-11-22 14:56
閱讀 1175·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