"not resolved from current namespace" 错误,从 R 调用 C 例程时

"not resolved from current namespace" error, when calling C routines from R

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

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

第一步:下载最新包源

从上面link下载1.8-17版本。当在 CRAN 上发布新的 mgcv 版本时,这样的 link 将会消失。但您可以随时前往 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。我们不需要担心 header 文件或 运行 时间 linking 到 BLAS 库。 Headers 在 mat.c 中可用,link 到 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);

我们还需要附加一个header文件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 session(可能 start-up 需要 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)

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

我现在有一个临时 "fix"。而不是

.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")