Perl XS:创建并 return 字符串数组 (char*) 取自调用 C 函数或失败时的 undef

Perl XS: create and return array of strings (char*) taken from calling a C function or undef on failure

我有 Perl XS 代码,它从外部 C 库调用一个函数 returns char **(字符串数组)。

XS 代码最终将 return 返回给 Perl 一个包含所有字符串结果的数组引用。或 undef 失败。

我有两个问题:

  1. 程序退出时,我得到一个核心转储,其中包含有关内存损坏、双重释放等消息(例如 double free or corruption (fasttop))。
  2. 如何 return 来自 XS sub 的 undef 值表示出现问题(不是空数组)?

此外,如果有人可以确认我正在正确处理从 Perl 到 C 函数的字符串是 utf8 编码的情况(例如输入文件名)或从 C 函数返回的结果(可能包含 utf8 字符串) ) 被发送回 Perl OK。

这是我的代码(根据 建模,如果我没看错,示例 #1):

AV *
decode(infilename_SV)
    SV *infilename_SV
  PREINIT:
    char *infilename;
    STRLEN infilename_len;
    char **results;
    size_t results_sz;
    char *aresult;
    size_t I;
    SV **aresultPP;
    char *dummy;
    STRLEN dummy_len;
  CODE:
    infilename = SvPVbyte(infilename_SV, infilename_len)
    // call C function
    results = myfunc(infilename, &results_sz);
    if( results == NULL ){
      printf("error!");
      // HOW TO return undef (and not an empty array?)
    }
    // create a Perl array to be returned
    RETVAL = (AV*)sv_2mortal((SV*)newAV());
    for(I=0;I<results_sz;I++){
      results_sz = strlen(results[I]);
      // create a new Perl string and copy this result
      aresult = newSVpv(results[I], 0);
      av_push(RETVAL, aresult);
      // free results as returned by C call
      free(results[I]);
    }
    // free results as returned by C call
    free(results);
    // debug print results
    for(I=0;I<results_sz;I++){
      aresultPP = av_fetch((AV *)RETVAL, I, 0);
      dummy = SvPVbyte(*apayloadPP, dummy_len);
      printf("result: %s\n", dummy);
    }
  OUTPUT:
     RETVAL

On program exit I get a core dump with messages about memory corruption, double free etc. (e.g. double free or corruption (fasttop)).

这可能是因为您覆盖了 for 中的循环变量 results_sz 导致了未定义的行为。

How to return an undef value from XS sub denoting that something went wrong (not an empty array)?

您可以 return &PL_sv_undef 表示未定义的值,请参阅 perlxs 了解更多信息。例如像这样:

SV *
decode(infilename_SV)
    SV *infilename_SV
  PREINIT:
    char *infilename;
    STRLEN infilename_len;
    char **results;
    size_t results_sz;
    char *aresult;
    size_t I;
  CODE:
    infilename = SvPVbyte(infilename_SV, infilename_len);
    results = myfunc(infilename, &results_sz);
    if( results == NULL ){
      RETVAL = &PL_sv_undef;
    }
    else {
       AV *av = newAV();
       for(I=0; I < results_sz; I++){
          aresult = newSVpv(results[I], 0);
          av_push(av, aresult);
          free(results[I]);
       }
       free(results);
       RETVAL = sv_2mortal(newRV_noinc((SV*)av));
    }
  OUTPUT:
     RETVAL

if anyone can confirm that I am handling correctly the cases where strings from Perl into the C function are utf8-encoded (e.g. the input filename)

要将 Perl UTF-8 字符串作为 UTF-8 编码字符串传递给 C-function,您可以使用 SvPVutf8() 而不是 SvPVbyte(),请参阅 perlguts想要查询更多的信息。示例:

infilename = SvPVutf8(infilename_SV, infilename_len);

or the results back from the C function (which may contain utf8 strings) are sent back to Perl

您可以使用 newSVpvn_flags() 而不是 newSVpvn() 将 UTF-8 编码的 C-string 转换为 Perl 字符串。例如:

aresult = newSVpvn_flags(results[I], strlen(results[I]), SVf_UTF8);