【问题标题】:"not resolved from current namespace" error, when calling C routines from R从 R 调用 C 例程时出现“未从当前命名空间解析”错误
【发布时间】:2017-09-25 12:42:52
【问题描述】:

我最近正在使用mgcv GAM 进行一些计算测试。修改了一些原有的功能,增加了一些。为了不破坏兼容性,对于我要修改的每个函数,我都会创建一个新版本,函数名称中带有 .zheyuan 后缀。例如,使用Sl.fit 函数进行惩罚最小二乘拟合,我将有一个Sl.fit.zheyuan。我会简单地将我编写的所有 R 函数收集到一个独立的 R 脚本“zheyuan.R”中。通过将此文件添加到mgcv_1.8-17 package source 的R 目录中,并将修改后的包编译到本地路径中,我可以将其加载用于测试目的。

我在添加 R 例程时没有问题,但在添加 C 例程时没有问题。安装修改后的包时没有发生错误,但是当我调用我添加的 C 例程的 R 包装函数时,我会收到问题标题中的错误。如果您对我的情况感兴趣,您可以按照以下步骤重现此类错误。

第一步:下载最新的包源

从以上链接下载 1.8-17 版本。当新的mgcv 版本在 CRAN 上发布时,此类链接将失效。但您可以随时前往mgcv CRAN page 下载最新版本。

让我们untar 来源。首先,删除文件MD5,这样我们在编译修改版本时就不会收到烦人的MD5警告。接下来,我们将在R目录和src目录中添加新内容。

第 2 步:创建 R 脚本

考虑以下 R 包装函数:

RX <- function (R, X) {
  X <- X + 0
  .Call("C_mgcv_RX", R, X)
  X
  }

创建一个R脚本“zheyuan.R”来放置这个函数,并将其添加到mgcv/R中。

第 3 步:添加 C 例程

有关矩阵计算的 C 例程通常位于 src/mat.c 下。所以让我们在这个脚本的末尾添加一个新函数:

void mgcv_RX (SEXP R, SEXP X) {
  int nrowX = nrows(X);
  int ncolX = ncols(X);
  double one = 1.0;
  F77_CALL(dtrmm)("l", "u", "n", "n", &nrowX, &ncolX, &one, REAL(R), &nrowX, REAL(X), &nrowX);
  }

这是一个简单的例程,将上三角矩阵R 与矩形矩阵X 相乘。输出矩阵将覆盖X。为此目的将调用 Level-3 BLAS dtrmm。我们不需要担心头文件或运行时链接到 BLAS 库。标头在mat.c 中可用,与BLAS 的链接由R 管理。

第四步:注册 C 例程

以上是不够的。 mgcv 中的每个 C 例程将出现在三个位置。例如,让我们尝试搜索原生 C 例程:

grep mgcv_RPPt mgcv/src/*
# mgcv/src/init.c:  {"mgcv_RPPt",(DL_FUNC)&mgcv_RPPt,3},
# mgcv/src/mat.c:void mgcv_RPPt(SEXP a,SEXP r, SEXP NT) {
# mgcv/src/mgcv.h:void mgcv_RPPt(SEXP a,SEXP r, SEXP NT);

我们还需要附加一个头文件mgcv.h,并在init.c中注册这个C例程。

让我们追加

void mgcv_RX (SEXP R, SEXP X);

到mgcv.h 的末尾,在init.c 内部,执行:

R_CallMethodDef CallMethods[] = {
  {"mgcv_pmmult2", (DL_FUNC) &mgcv_pmmult2,5},
  {"mgcv_Rpiqr", (DL_FUNC) &mgcv_Rpiqr,5}, 
  {"mgcv_tmm",(DL_FUNC)&mgcv_tmm,5}, 
  {"mgcv_Rpbsi",(DL_FUNC)&mgcv_Rpbsi,2},
  {"mgcv_RPPt",(DL_FUNC)&mgcv_RPPt,3},
  {"mgcv_Rpchol",(DL_FUNC)&mgcv_Rpchol,4},
  {"mgcv_Rpforwardsolve",(DL_FUNC)&mgcv_Rpforwardsolve,3},
  {"mgcv_Rpcross",(DL_FUNC)&mgcv_Rpcross,3},
  {"mgcv_RX",(DL_FUNC)&mgcv_RX,2},  //  we add this line
  {NULL, NULL, 0}
};

第 5 步:编译和加载

tar将mgcv文件夹修改为mgcv.tar.gz。

打开一个新的、干净的 R 会话(可能需要R --vanilla 来启动)。然后指定本地库路径并运行:

path <- getwd()  ## let's just use current working directory
## make sure you move "mgcv.tar.gz" into current working path
install.packages("mgcv.tar.gz", repos = NULL, lib = path)
library(mgcv, lib.loc = path)

第 6 步:测试并得到错误

R <- matrix(runif(25), 5)
R[lower.tri(R)] <- 0
X <- matrix(runif(25), 5)
mgcv:::RX(R, X)  ## function is not exported, so use `mgcv:::` to find it

# Error in .Call("C_mgcv_RX", R, X) : 
#   "C_mgcv_RX" not resolved from current namespace (mgcv)

谁能解释一下为什么以及如何解决这个问题?

【问题讨论】:

    标签: c r namespaces r-package mgcv


    【解决方案1】:

    我现在有一个临时的“修复”。而不是

    .Call("C_mgcv_RX", R, X)
    

    使用以下任意一种:

    .Call(mgcv:::"C_mgcv_RX", R, X)
    .Call(getNativeSymbolInfo("mgcv_RX"), R, X)
    

    我之所以想到这个,是因为我突然意识到::: 也可以提取 C 例程。既然包编译成功了,那mgcv:::是没有办法找不到这个C例程的。是的,它有效。


    要检查我们定义的 C 例程在加载的共享库中是否可用,请尝试

    is.loaded("mgcv_RX")
    # TRUE
    

    要列出加载的共享库中所有已注册的 C 例程,请使用

    getDLLRegisteredRoutines("mgcv")
    

    【讨论】:

      猜你喜欢
      • 2014-01-25
      • 1970-01-01
      • 2015-09-29
      • 1970-01-01
      • 1970-01-01
      • 2013-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多