首页 > 解决方案 > 寻找一种从辅助函数调用 Perl XS C API 函数/宏的方法

问题描述

我一直在试验 Perl XS C API 并遇到了障碍。

我在下面简化了我的示例。假设一个现有的 struct MyObject 然后访问属性“a”或“b”并为其中任何一个创建哈希,我可以使用以下代码:

typedef struct {
   const char *prop_a;
   const char *prop_b;
   struct {
    const char **items;
    int num;
   } names;
}   MyObjectInfo;

typedef MyObjectInfo *MyObject;

MODULE = my_obj            PACKAGE = MyObject    PREFIX = my_obj_

SV *
my_obj_a(o)
   MyObject o

   CODE:
   SV *info = newHV();
   hv_store(info, “a”, 1, newSVpvn(o->prop_a, strlen(o->prop_a)), 0);
   int i;
   for(i = 0; i < o->names.num; i++) {
        const char *n = o->names.items[i];
        hv_store(info, n, strlen(n), newSViv(i), 0);
   }
   RETVAL = sv_2mortal(newrv_noinc(val));
 
   OUTPUT:
   RETVAL

SV *
my_obj_b(o)
   MyObject o

   CODE:
   SV *info = newHV();
   hv_store(info, “b”, 1, newSVpvn(o->prop_b, strlen(o->prop_b)), 0);
   int i;
   for(i = 0; i < o->names.num; i++) {
        const char *n = o->names.items[i];
        hv_store(info, n, strlen(n), newSViv(i), 0);
   }
   RETVAL = sv_2mortal(newrv_noinc(val));
 
   OUTPUT:
   RETVAL

我想做的是在这样的实用程序函数中共享一些功能

SV *create_obj_hash(MyObjectInfo *o, const char *k, const char *p) {
    SV *val = newHV();
    hv_store(val, k, strlen(k), newSVpvn(p, strlen(p)), 0);
    int i;
    for(i = 0; i < o->names.num; i++) {
        const char *n = o->names.items[i];
        hv_store(info, n, strlen(n), newSViv(i), 0);
    }
    return val;
}

MODULE = my_obj            PACKAGE = MyObject    PREFIX = my_obj_

SV *
my_obj_a(o)
   MyObject o

   CODE:
   SV *info = create_obj_hash(o, “a”, o->prop_a);
   RETVAL = sv_2mortal(newrv_noinc(val));
 
   OUTPUT:
   RETVAL

SV *
my_obj_b(o)
   MyObject o

   CODE:
   SV *info = create_obj_hash(o, “b”, o->prop_b);;
   RETVAL = sv_2mortal(newrv_noinc(val));
 
   OUTPUT:
   RETVAL

但是,当我在 create_obj_hash() 中进行宏扩展时,会出现以下消息。

myobj.xs: In function 'create_obj_hash':
/usr/lib/x86_64-linux-gnu/perl/5.28/CORE/perl.h:175:16: error: 'my_perl' undeclared (first use in this function); did you mean 'my_fork'?
 #  define aTHX my_perl
                ^~~~~~~
ppport.h:6145:41: note: in definition of macro 'MUTABLE_PTR'
 #  define MUTABLE_PTR(p) ({ void *_p = (p); _p; })
                                         ^
/usr/lib/x86_64-linux-gnu/perl/5.28/CORE/hv.h:651:17: note: in expansion of macro 'MUTABLE_HV'
 #define newHV() MUTABLE_HV(newSV_type(SVt_PVHV))
                 ^~~~~~~~~~
/usr/lib/x86_64-linux-gnu/perl/5.28/CORE/perl.h:188:18: note: in expansion of macro 'aTHX'
 #  define aTHX_  aTHX,
                  ^~~~
/usr/lib/x86_64-linux-gnu/perl/5.28/CORE/embed.h:532:40: note: in expansion of macro 'aTHX_'
 #define newSV_type(a)  Perl_newSV_type(aTHX_ a)
                                        ^~~~~
/usr/lib/x86_64-linux-gnu/perl/5.28/CORE/hv.h:651:28: note: in expansion of macro 'newSV_type'
 #define newHV() MUTABLE_HV(newSV_type(SVt_PVHV))
                            ^~~~~~~~~~
myobj.xs:42:19: note: in expansion of macro 'newHV'
     return (void*)newHV();

非常感谢你,布赖恩

标签: cperlxs

解决方案


首先,您可能缺少以下部分或全部内容:

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

主要问题是您没有为API 调用提供上下文。

某些 Perl 版本允许进程同时运行多个解释器实例。如果-Dmultiplicity在创建 Perl 时使用,则构建将支持这一点。(您可以使用 . 进行检查perl -V:usemultiplicity。)-Dmultiplicity暗示了-Dusethreads构建perl带有线程支持的选项(因为为每个线程创建了一个解释器实例)。

因此,大量的 Perl API 调用需要调用者提供一个上下文(“THX”)来标识要使用的解释器。将解释器视为一个对象(在 OOP 的意义上),将上下文视为调用者。

在 XS 代码中,会自动为您创建一个包含上下文的变量。该变量通过使用宏自动传递给 Perl API 调用。

#define newSVpvn(a,b) Perl_newSVpvn(aTHX_ a,b)
//                                  ^^^^^
//                      Causes the context to be passed
//                      to Perl_newSVpvn, the real name
//                      of newSVpvn.

newSVpvn因此,您需要上下文来完成这项工作(无论Perl_newSVpvn您使用哪个)。要获取上下文,请使用以下宏:

  • 如果你的函数除了上下文没有参数,
    • 用作pTHX函数声明的第一个参数。
    • 用作aTHX函数调用的第一个参数。
  • 如果你的函数除了上下文还有参数,
    • 用作pTHX_函数声明的第一个参数。
    • 用作aTHX_函数调用的第一个参数。

p”代表“参数”,“ a”代表“参数”,“ _”代表逗号。

在你的情况下,你会使用

STATIC SV *create_obj_hash(pTHX_ MyObjectInfo *o, const char *k, const char *p) {
#define create_obj_hash(a,b,c) create_obj_hash(aTHX_ a,b,c)
    ...
}

感谢#define,您可以继续使用

SV *info = create_obj_hash(o, "b", o->prop_b);

未经测试。让我知道是否有任何问题。


推荐阅读