【问题标题】:Calling the agrep .Internal C function from Rcpp从 Rcpp 调用 agrep .Internal C 函数
【发布时间】:2021-02-17 03:28:18
【问题描述】:

简而言之:如何在 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 目标中的每一个检查数百万个模式时。

【问题讨论】:

  • 简而言之,您不能轻易或直接作为@Oliver 详细说明:R 根本不公开这些内部函数。但是,例如,请参阅 Rcpp Gallery article on using Boost regex with Rcpp -- 您可以以同样的方式找到任何其他正则表达式库。
  • @DirkEddelbuettel 我明白了,谢谢!快速跟进:正如我对@Oliver 所说,使用do_agrep 或其他可能在src/include/Internal.h 中声明的东西怎么样?
  • 仍然没有,因为 R Core 将所有这些功能标记为“隐藏”。如果您更仔细地查看我在 CRAN 上的(有争议的大型)包集,您会发现两个 RApi*,因为需要将代码复制出来以“取消隐藏”它。那时,您还可以按照我之前的评论建议使用通用 reg.exp 引擎。祝你好运!

标签: r c rcpp agrep


【解决方案1】:

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

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

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

这种方法的问题:

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

如果您愿意走这么远,我建议您仔细阅读adv-R "R's C interface" 和Chapter 2, 5 and 6 of R-ext 甚至可能是R internal manual,最后在完成后看看@ 来自@ 的do_docall 987654346@ 并将其与我的存储库cmdline.arguments/src/utils/{cmd_coerce.h, cmd_coerce.c} 中的实现进行比较。在这个版本中,我有

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

当我转移到另一个分支时,这个实现将“永远”冻结(如果我想再次沿着这条路走下去,这个实现会为了我自己的未来利益而冻结)。

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

如果您想使用其他包中的非公开代码,则再次使用此方法。虽然通常在这里它就像“复制粘贴”他们的文件到您的存储库一样简单。

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

【讨论】:

  • 非常感谢这个有见地的回答!我脑海中留下的最后一个疑问是关于你一开始所说的:“除非该函数在“src/include/”中的一个头文件中可见”。由于这些函数是在src/include/Internal.h 中声明的,那么是否有可能以某种方式通过do_agrep 函数(以及FWIW 你过去想要的do_docall)来做我所要求的事情?
  • 正如 Dirk 在评论中所说,这是一个不。如果你查看 R 的源代码,你会发现一些东西一次又一次地出现。 SEXP attribute_hidden ....。 attribute_hidden 是个大问题。您可以再次按照我解释的步骤进行操作。看看do_agrep 并没有那么复杂。但我认为,正如 Dirk 建议的那样,更简单的方法是使用 std::regex 或 boost::regex。我自己的代码依赖于std::regex 的价值。虽然我没有执行任何模糊正则表达式搜索。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-13
  • 1970-01-01
相关资源
最近更新 更多