问题描述
简而言之:如何从 Rccp C++ 代码中调用 agrep
C 内部函数,该函数在用户使用基本 R 中的常规 agrep
函数时被调用?
总而言之:我在这里发现了多个关于如何从 Rcpp 中调用为另一个包创建的 C 或 C++ 函数的问题(例如 using C function from other package in Rcpp 和Rcpp: Call C function from a package within Rcpp)。
然而,我想要实现的事情同时更简单,但文档也更少:它是直接从 Rcpp 内部调用一个 .Internal C 函数使用基础 R 而不是另一个包,没有与 R 接口(即,没有执行 Call R functions in Rcpp 中所说的)。对于位于基础 R 的 agrep 包装器下方的 .Internal C 函数,我怎么能做到这一点?
我在这里尝试调用的特定函数是 agrep 内部 C 函数。对于上下文,我最终想要实现的是加快对 agrep 的调用,以便在必须针对数百万个 x 目标中的每一个检查数百万个模式时。
解决方法
好问题。它的长短是“你不能”(在许多情况下),除非该函数在“src/include/”中的头文件之一中可见。至少没那么容易。
不久前我遇到了一个类似的有趣挑战,我试图访问 do_docall
函数(由 do.call
调用),这不是一项简单的任务。首先,不能直接只用 #include <agrep.c>
(或类似的东西)。该文件根本无法包含,因为它不是“src/include”的一部分。它被编译并删除未编译的文件(更不用说永远不应该“包含”一个 .c 文件)。
如果愿意继续努力,那么下一步可以考虑“复制”和“更改”源代码。基本上在“src/main/agrep.c”中找到该函数,将其复制到您的包中,然后修复您发现的任何错误。
这种方法的问题:
- 如
R-exts
中所述,sexprec_info
的内部结构未公开(这是 R 中所有对象的基本结构)。许多内部函数使用此结构中的字段,因此必须将该结构“复制”到您的源代码中,以便专门将其公开给您的代码。 - 如果您在此文件之前
#include <Rcpp.h>
,则需要遍历对内部函数的每一次调用,并可能添加R_
或Rf_
。 - 该函数可能包含对其他“内部”函数的调用,这些函数需要进一步复制和更改才能工作。
- 您还需要清楚地了解
CDR
、CAR
和类似的功能。内部函数有一个文档化的结构,其中第一个参数包含传递给函数的完整调用,并且像这两个函数一样用于访问调用的部分。 我自己做了一个可靠的并重写了do_docall
更改输入格式,以避免必须考虑这一点。但这需要时间。另一种方法是根据文档创建一个pairlist
,将其类型设置为 call-sexp(目前我已经失去了确切的名称)并为op
、{{ 1}} 和args
。 - 最后,如果你通过这些步骤,发现有必要复制
env
的内部结构(如下所述),那么你需要非常 当你包含sexprec_info
和Rinternals
时要小心,因为如果你包含你的标题和这些,其中任何一个都会导致你的代码以最漂亮和安静的方式崩溃和燃烧顺序错误!请注意,这甚至适用于Rcpp
,结果可能确实以错误的任意顺序包含它们!
如果您愿意深入到排水沟中,我建议您仔细阅读 adv-R "R's C interface" 和 Chapter 2,5 and 6 of R-ext 甚至可能是 R internal manual,最后阅读一下来自 [[Rcpp::export]]
的 do_docall
并将其与我的存储库 cmdline.arguments/src/utils/{cmd_coerce.h,cmd_coerce.c} 中的实现进行比较。在这个版本中我有
- 添加了所有非公开的内部结构,以便我可以访问它们的未修改表单(当前会话未修改)。
- 这包括用于存储当前使用的
src/main/coerce.c
的表,该表用作查找。这导致了一个问题,因为我无法访问修改后的 版本,因此我的代码略有改动,旧代码被宏SEXP
阻止。幸运的是,导致问题的代码有一个静态答案,所以我可以解决这个问题(但情况可能并非总是如此)。
- 这包括用于存储当前使用的
- 我添加了很多
#if --- defined(CMDLINE_ARGUMENTS_MAYBE_IN_THE_FUTURE)
,因为它们的宏版本不可用(因为我在某个时候Rf_
) - 代码已被拆分为更小的函数,以使其更具可读性(为了我自己)。
- 该函数有一个额外的参数(名称),它没有在内部函数中使用,并增加了一些错误(针对我的特定需要)。
当我转移到另一个分支时,这个实现将“永远”被冻结(如果我想再次走上这条路,这个实现将被冻结,这是为了我自己未来的利益)。
>我花了几天时间在互联网上搜索相关信息,发现了 2 个不同的帖子,讨论如何实现这一点,我的方法基本上复制了这一点。在 cran 包中是否真的允许这样做,是另一个问题(而不是我将要测试的问题)。
如果您想使用其他包中的非公开代码,则再次使用此方法。虽然通常在这里,它就像将他们的文件“复制粘贴”到您的存储库中一样简单。
作为最后一点,您提到的目的是“加速”您的代码,以便在您必须对 #include <Rcpp.h>
执行数百万次调用时。似乎这是一个应该考虑并行执行任务的时候。即使在完成上述步骤之后,创建 N 个并行会话来处理 K 个评估(比如 100.000),将是减少计算时间的第一步。当然,每个会话都应该有一个批处理,而不是一次调用 agrep
。