为什么在 XSUB 的输出变量上包含 SvSETMAGIC()?

Why include SvSETMAGIC() on output variables in an XSUB?

阅读perlxs documentation,我来到了关于OUTPUT关键字的部分:

xsubpp emits an automatic SvSETMAGIC() for all parameters in the OUTPUT section of the XSUB, except RETVAL. This is the usually desired behavior, as it takes care of properly invoking 'set' magic on output parameters (needed for hash or array element parameters that must be created if they didn't exist).

我不确定我是否理解为什么需要 set 魔法(以及为什么 RETVAL 不需要它)?为什么散列和数组元素参数需要 set 魔法?

Perl 的所有数据结构都支持魔术,而不仅仅是 SVs(尽管名称如此),特别是对于散列和数组,这是 tie 机制或 fieldhash 它在散列条目级别实现了弱引用的模拟。

由于 OUTPUT 指令指示哪些参数可能会被 XSUB 的 C 主体修改,并且可能会传入包含 set magic 的变量,因此根据类型映射设置值而不调用 set处理程序可能会导致不一致的行为。

use Scalar::Util qw(weaken);

my $foo;
my $ref = $foo;
weaken($ref);

作为magic的一个例子,weaken减少了$foo的引用计数,并添加magic指向回$ref,以便在$foo时清除它收集垃圾。

此外,它还为$ref添加了set magic,以拆除此反向引用,否则当$foo被销毁时,$ref将被清除,即使此时它是不再指向 $foo.

如果您使用 $ref 作为参数,它会在堆栈上产生别名(这就是 $_[0] is assignable 的原因):

modifies_arguments($ref);

sub modifies_arguments {
    $_[0] = "blah"; # set magic is invoked to tear down the back referencing
} 

如果 modifies_arguments 是一个纯 Perl,很容易看出为什么这是合适的,但是关于正确性的相同假设当然必须适用于 XSUB,这就是为什么 OUTPUT 被用来标记哪个arguments 会将它们的值设置为 C 级参数变量在函数体末尾的任何值,并触发 set magic。

这不适用于 RETVAL,因为这在技术上不是一个赋值,而是将一个新的 SV 压入堆栈,并且任何设置魔法都将在函数 returns 之后处理赋值操作(如果有的话)。

很简单。每当你分配给一个标量时,你需要在它之后调用 SvSETMAGIC() 以防它有与之相关的魔法。

分配给 RETVAL 不会分配给 Perl 变量,因此调用 SvSETMAGIC(RETVAL)(除非您实际修改了 RETVAL)是错误的。如果返回值被赋值给调用者中的另一个标量,则赋值将在赋值前对返回值调用SvGETMAGIC,并在赋值后对赋值变量调用SvSETMAGIC