
在
第一部分中 ,讲述了有关设置工具和一般概念的基本知识。
可以说,
第二部分是关于弹丸,思想,计划,计划的第一种方法。
在本文中,有关C和K / N互操作,更多的宏,痛苦,绝望和“善良之光”的内容将更加严格。 当然,会有一章讲述有关成就的故事(您不会称赞自己……此外,还有关于史诗般的fakap的故事。
免责声明:在为PHP编写库的上下文中考虑了以下所有内容。
第一章 天真烂漫
在循环的第一部分中介绍了如何在C中使用K / N函数。 因此,在这里我将告诉您如何在K / N中使用C函数。
官方文档相当小巧简洁,但是对于简单的项目来说,已经足够了。
简而言之,您需要创建一个扩展名为
.def的特殊文件,并在其中指定必要的头文件。
headers = php.h
然后将其提供给名为
cinterop的程序。
# cinterop -def php.def -o php
在输出中,您将获得包含llvm位代码和各种元信息的
libphp.klib库。
然后,您可以安全地使用头文件(
#define
)中描述的函数和宏,而不必忘记在编译阶段连接库。
# kotlinc -opt -produce static ${SOURCES} -l libphp.klib -o myLib
但是有细微差别。 没有一个。
以上述形式,库将不会被组装
怎么了 但是因为以下行出现在php.h中:
#include "php_version.h" #include "zend.h" #include "zend_sort.h" #include "php_compat.h" #include "zend_API.h"
在这里应该注意,llvm仍在编译库,它具有
-I开关 ,而cinterop具有
-copt开关 。 好吧,你明白了。 结果,这样的命令足以编译
php.h。 # cinterop -def my.def -o myLib -I${PHP_LIB_ROOT} -copt -I${PHP_LIB_ROOT} \ -copt -I${PHP_LIB_ROOT}/main \ -copt -I${PHP_LIB_ROOT}/Zend \ -copt -I${PHP_LIB_ROOT}/TSRM
宏。 我爱你,恨你! 不,我只是讨厌。
关于C> K / N互操作,您需要了解的
#define
是
每个扩展为常数的C宏都表示为Kotlin属性。 不支持其他宏。
然后我们回想起PHP扩展是宏上的一个宏,它驱动一个宏,并尽量不要哭泣。
但是,并非一切都那么糟糕。 为了解决这种情况,K / N开发人员提供了一卷蓝色电气胶带,用于粘贴到
自定义声明 def文件。 看起来像这样(例如,使用宏
Z_TYPE_P
)
headers = php.h --- static inline zend_uchar __zp_get_arg_type(zval *z_value) { return Z_TYPE_P(z_value); }
现在在K / N代码中可以使用
__zp_get_arg_type
函数
第二章 PHP INI设置或子子宏。
这是对PHP源代码的一种“美好的祝愿”。
有4个宏可用于提取设置:
INI_INT(val) INI_FLT(val) INI_STR(val) INI_BOOL(val)
其中
val
是带有设置名称的字符串。
现在,让我们看一下
INI_STR
的示例,以了解如何定义此宏。
#define INI_STR(name) zend_ini_string_ex((name), sizeof(name)-1, 0, NULL)
已经注意到他的“致命缺陷”了吗?
如果没有,让我告诉您-这是
sizeof
函数。 当直接使用宏时,一切都很好:
php_printf("The value is : %s", INI_STR("my.ini"));
当您通过
.def文件中的代理函数使用它时,笔架变成南瓜,并且
sizeof(名称)返回指针的大小。 将军科特林原生。
实际上,规避只有两种选择。
- 不要使用宏,而要使用它们所附加的功能。
- 每个必需设置的硬包装程序功能。
第一个选项对每个人都比第二个选项更好,除了一点以外-没有人可以保证宏声明不会更改。 因此,对于我的项目,我感到非常不满,选择了第二个选项。
第三章 调试? 什么行李
行动1-互操作。
曾经,在将蓝带附加到20个连续代理功能的def文件之后,我收到了一个奇妙的错误。
Exception in thread "main" java.lang.Error: /tmp/tmp399964332777824085.c:103:38: error: too many arguments to function call, expected 2, have 3 at org.jetbrains.kotlin.native.interop.indexer.UtilsKt.ensureNoCompileErrors(Utils.kt:137) at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.indexDeclarations(Indexer.kt:902) at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.buildNativeIndexImpl(Indexer.kt:892) at org.jetbrains.kotlin.native.interop.indexer.NativeIndexKt.buildNativeIndex(NativeIndex.kt:56) at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.processCLib(main.kt:283) at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.interop(main.kt:38) at org.jetbrains.kotlin.cli.utilities.InteropCompilerKt.invokeInterop(InteropCompiler.kt:100) at org.jetbrains.kotlin.cli.utilities.MainKt.main(main.kt:29)
对一半发表评论,然后重新编译,如果对其余一半发表评论,则我们收集...并且鉴于编译标头的过程足够长...(是的,这似乎比十二个源文件的爬取要快,并且用放大镜精心地调和)。
第二个“美好之光”进入了JetBrains。
行动2-运行时。
我遇到运行时
分段错误 。 好吧,它发生了。 我正在进入调试器。 嗯... STA?
Program received signal SIGSEGV, Segmentation fault. kfun:kotlinx.cinterop.toKString@kotlinx.cinterop.CPointer<kotlinx.cinterop.ByteVarOf<kotlin.Byte>>.()kotlin.String () at /opt/buildAgent/work/4d622a065c544371/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt:402 402 /opt/buildAgent/work/4d622a065c544371/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt: No such file or directory.
第四章 我将茶倒入您的茶中,以便您可以在喝茶的同时喝茶。
在这里有必要告诉我我所做的那种胡扯。
您编写一个描述未来PHP扩展的DSL,编写包含功能,类和方法的实现的K / N代码,然后运行
make
,然后奇迹般地获得可以连接到PHP的现成的库。
组装可分为4个阶段:
- 在C和K / N之间创建一个图层(相同的
cinterop
) - 扩展C代码生成
- 用逻辑编译库
- 编译目标库
目的是增加以K / N代码创建PHP类实例的功能。 例如,以便该类可以定义
getInstance()
方法。 我想制造它以便于使用。
在C语言中,此问题解决了一次或两次。
zval *obj = malloc(sizeof(zval)); object_init_ex(obj, myClass);
这看起来很简单-将其转移到K / N,但这是
myClass
...
但是
myClass
是
zend_class_entry*
类型的全局变量,在项目的C代码中声明,并且事先具有未知名称。
小心你的手。 您需要从K / N代码编译该库,其中将有一个函数需要访问
myClass
,该函数在生成的但未编译的C代码中定义,然后从该代码中调用该函数。
最终,此功能的实现导致添加了两个新工件:代码生成阶段的
.h和
.kt ,cinterop阶段的复杂性和史诗般的phakap,我将在最后讨论。
第五章 我叫什么名字?
为什么的故事:
enum class ArgumentType { PHP_STRING, PHP_LONG, PHP_DOUBLE, PHP_NULL, ... }
优于:
enum class ArgumentType { STRING, LONG, DOUBLE, NULL, ... }
是的,甚至无需解释。 这是
ArgumentType.NULL
在Kotlin库的头文件中变成的内容:
struct { extension_kt_kref_php_extension_dsl_ArgumentType (*get)(); } NULL;
这就是gcc对此的反应。
/root/simpleExtension/phpmodule/extension_kt_api.h:113:17: error: expected identifier or '(' before 'void' } NULL; ^
窗帘! 当心名字。
倒数第二章。 您不会赞美自己-没有人会赞美您。
总的来说,我已经实现了我的目标。 沉浸在该主题中的“框架”通常就可以在Kotlin Native上编写PHP扩展了。 仍然需要添加一些(而不是最关键的)功能和修饰。
我希望该项目本身以及很好的文档可以
在github上查看。
关于K / N我能说些什么? 很好 在上面写东西是一种享受,而浅浅的沙哑和粗糙可以归因于他甚至还没有爬出摇篮的事实:)
最后一章。 光线很好,没有引号。
现在,我要非常认真并深切地感谢JetBrains的家伙和Kotlin Native松弛频道的居民。 你真厉害!
还要特别感谢
Nicholas Igotti 。
红利 史诗般的Fakap。
上下文在第四章中进行了描述。
实际上,当将所有内容添加到可以无错误编译的状态时,就会出现问题-在测试期间,PHP从一个完全陌生的角度向我开放。
# php -dextension=./phpmodule/modules/extension.so -r "var_dump(ExampleClass::getInstance());" *RECURSION* #
“菲加斯!” -我想,我进入了PHP源代码,发现了这样的片段。
case IS_OBJECT: if (Z_IS_RECURSIVE_P(struc)) { PUTS("*RECURSION*\n"); return; }
添加调试:
printf("%u", Z_IS_RECURSIVE_P(struc))
导致:
undefined symbol: Z_IS_RECURSIVE_P in Unknown on line 0
“菲加斯!” 我又想了。
那时,我猜想看一下Linux主机上实际使用的php.h(7.1.8),而不是从早午餐(7.3.x)的github上拉来的那个。 直as愧。
但是,事实证明,它不是梭芯。
在我控制的对象生命的所有阶段,正确的递归检查代码报告一切正常。 这意味着您应该仔细查看那些我无法控制的地方。 恰好有一件事-将我的对象返回给
var_dump
函数
RETURN_OBJ( example_symbols()->kotlin.root.php.extension.proxy.objectToZval( example_symbols()->kotlin.root.exampleclass.getInstance() ) )
将宏RETURN_OBJ打开到最后。 远离监护人紧张和怀孕!
1) RETURN_OBJ(r) 2) { RETVAL_OBJ(r); return; } 3) { ZVAL_OBJ(return_value, r); return; } 4) { do { zval *__z = (return_value); Z_OBJ_P(__z) = (r); Z_TYPE_INFO_P(__z) = IS_OBJECT_EX; } while (0); return; } 5) { do { zval *__z = (return_value); Z_OBJ(*(__z)) = (r); Z_TYPE_INFO(*(__z)) = (IS_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)); } while (0); return; } 6) { do { zval *__z = (return_value); (*(__z)).value.obj = (r); (*(__z)).u1.type_info = (8 | ((1<<0) << 8)); } while (0); return; }
在这里,我第二次感到as愧。 我完全
zend_object*
,将
zval*
推到
zend_object*
等待的地方,花了将近两天的时间查找错误。
谢谢大家科特林! :)
PS。 如果有一个善良的人能读懂我笨拙的英语并更正文档,那么我的感激之情将无止境。