从 Rcpp 调用 agrep .Internal C 函数

Calling the agrep .Internal C function from Rcpp

简而言之: 我如何从 Rccp C++ 代码中调用 agrep 当用户使用常规 agrep 基数 R 的函数?

In long: 我在这里发现了多个关于如何从 Rcpp 中调用为另一个包创建的 C 或 C++ 函数的问题(例如 using C function from other package in RcppRcpp: Call C function from a package within Rcpp).

然而,我试图实现的事情同时更简单,但记录也更少:它是 直接 从 Rcpp 中调用,一个 .基础 R 而不是另一个包附带的内部 C 函数, 没有 与 R 接口(也就是说,没有做 Call R functions in Rcpp 中所说的)。我怎么能为位于基础 R 的 agrep 包装器下面的 .Internal C 函数做到这一点?

我在这里尝试调用的特定函数是 agrep 内部 C 函数。对于上下文,我最终想要实现的是加快对 agrep 的调用,以便在必须针对数百万个 x 目标中的每一个分别检查数百万个模式时。

好问题。简而言之就是“你不能”(在很多情况下),除非该函数在“src/include/”中的 header 文件之一中可见。至少没那么容易。

不久前我遇到了一个类似的有趣挑战,我试图访问 do_docall 函数(由 do.call 调用),这不是一项简单的任务。首先,不可能直接 #include <agrep.c> (或类似的东西)。该文件根本无法包含在内,因为它不是“src/include”的一部分。它被编译并删除未编译的文件(更不用说永远不应该“包含”.c 文件)。

如果愿意努力,那么下一步可以考虑“复制”和“更改”源代码。基本上在“src/main/agrep.c”中找到该函数,将其复制到您的包中,然后修复您发现的任何错误。

这种方法的问题:

  1. R-exts 中所述,sexprec_info 的内部结构不是 public(这是 R 中所有 objects 的基本结构)。许多内部函数使用此结构中的字段,因此必须将结构“复制”到您的源代码中,以使其 public 专门针对您的代码。
  2. 如果您曾经 #include <Rcpp.h> 在此文件之前,您将需要检查对内部函数的每一次调用,并可能添加 R_Rf_.
  3. 该函数可能包含对其他“内部”函数的调用,需要进一步复制和更改才能正常工作。
  4. 您还需要清楚地了解 CDRCAR 和类似内容的作用。内部函数有一个记录的结构,其中第一个参数包含传递给函数的完整调用,并且像那些 2 的函数用于访问部分调用。 我自己做了一个坚实的工作并重写了 do_docall 更改输入格式,以避免不得不考虑这一点。但这需要时间。另一种方法是根据文档创建一个 pairlist,将其类型设置为 call-sexp(目前我不知道确切的名称)并为 op 传递适当的参数, argsenv.
  5. 最后,如果你通过这些步骤,发现需要复制 sexprec_info 的内部结构(如后所述),那么你将需要 非常 当你包含 RinternalsRcpp 时要小心,因为其中任何一个都会导致你的代码在最美丽和安静的情况下崩溃和烧毁如果您以错误的顺序包含 header 和这些!请注意,这甚至适用于 [[Rcpp::export]],这可能确实会以错误的任意顺序包含它们!

如果你愿意走到这一步,我建议你仔细阅读adv-R "R's C interface" and Chapter 2, 5 and 6 of R-ext and maybe even the R internal manual, and finally once that is done take a look at do_docall from src/main/coerce.c and compare it to the implementation in my repository cmdline.arguments/src/utils/{cmd_coerce.h, cmd_coerce.c}。在这个版本中我有

  1. 添加了所有不是 public 的内部结构,以便我可以访问它们的 unmodified 形式(未经修改当前 session)。
    • 这包括用于存储当前使用的 SEXP 的 table,用作查找。这导致了一个问题,因为我无法访问 modified 版本,所以我的代码被宏 #if --- defined(CMDLINE_ARGUMENTS_MAYBE_IN_THE_FUTURE)。幸运的是,导致问题的代码有一个静态答案,所以我可以解决这个问题(但情况可能并非总是如此)。
  2. 我添加了很多 Rf_s 因为他们的宏版本不可用(因为我 #include <Rcpp.h> 在某些时候)
  3. 代码已被拆分成更小的函数以使其更具可读性(为了我自己)。
  4. 该函数有一个额外的参数(名称),它在内部函数中没有使用,但有一些错误(针对我的特定需要)。

随着我转到另一个分支,此实现将“永远”冻结(如果我想再次走这条路,这个实现将被冻结是为了我自己的未来利益)。

我花了几天时间在互联网上搜索这方面的信息,发现了 2 个不同的帖子,讨论了如何实现这一点,我的方法基本上照搬了这个。这在 cran 包中是否真的被允许,是另一个问题(而不是我要测试的问题)。

如果您想使用来自其他包的 not-public 代码,请再次使用此方法。虽然通常在这里它就像“copy-paste”他们的文件到您的存储库一样简单。

作为最后的旁注,您提到的目的是在必须对 agrep 执行数百万次调用时“加速”您的代码。看来这是一个应该考虑并行执行任务的时候了。即使在完成上述步骤之后,创建 N 个并行 sessions 来处理 K 个评估(比如 100.000),将是减少计算时间的第一步。当然,每个 session 都应该分批次,而不是单次打电话给 agrep.