4

所以这里有点疯狂。

如果您有一些由 R 函数(作为共享对象)调用的 C 代码,请尝试将其添加到代码中

void warn() {

 int i; // just so the function has some work, but you could make it empty to, or do other stuff

}

如果您随后调用warn()R 函数调用的 C 代码中的任何位置,则会出现段错误;

  *** caught segfault ***
 address 0xa, cause 'memory not mapped'
Traceback:
  1: .C("C_function_called_by_R", as.double(L), as.double(G), as.double(T), as.integer(nrow),     as.integer(ncolL), as.integer(ncolG), as.integer(ncolT),     as.integer(trios), as.integer(seed), as.double(pval), as.double(pval1),     as.double(pval2), as.double(pval3), as.double(pval4), as.integer(ntest),     as.integer(maxit), as.integer(threads), as.integer(quietly))
  2: package_name::R_function(L, G, T, trios)
  3: func()
  4: system.time(func())
  5: doTryCatch(return(expr), name, parentenv, handler)
  6: tryCatchOne(expr, names, parentenv, handlers[[1L]])
  7: tryCatchList(expr, classes, parentenv, handlers)
  8: tryCatch(expr, error = function(e) {    call <- conditionCall(e)    if (!is.null(call)) {        if (identical(call[[1L]], quote(doTryCatch)))             call <- sys.call(-4L)        dcall <- deparse(call)[1L]        prefix <- paste("Error in", dcall, ": ")        LONG <- 75L        msg <- conditionMessage(e)        sm <- strsplit(msg, "\n")[[1L]]        w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w")        if (is.na(w))             w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L],                 type = "b")        if (w > LONG)             prefix <- paste(prefix, "\n  ", sep = "")    }    else prefix <- "Error : "    msg <- paste(prefix, conditionMessage(e), "\n", sep = "")    .Internal(seterrmessage(msg[1L]))    if (!silent && identical(getOption("show.error.messages"),         TRUE)) {        cat(msg, file = stderr())        .Internal(printDeferredWarnings())    }    invisible(structure(msg, class = "try-error", condition = e))})
  9: try(system.time(func()))
 10: .executeTestCase(funcName, envir = sandbox, setUpFunc = .setUp,     tearDownFunc = .tearDown)
 11: .sourceTestFile(testFile, testSuite$testFuncRegexp)
 12: runTestSuite(testSuite)
 aborting ...
 Segmentation fault (core dumped)
 (END)

不用说,如果您从 C 或 C++ 包装器而不是 R 函数调用相同的函数,则代码运行良好。如果您重命名warn()它也可以正常工作。

有任何想法吗?这是受保护的名称/符号吗?有这样的名字列表吗?我在 Ubuntu 12.01(i686-pc-linux-gnu(32 位))上使用 R 版本 2.14.1。C 代码使用 GNU GCC 4.6.3 编译。

4

1 回答 1

5

这似乎是一个非常有趣的问题。这是我的最小示例,在test.c我拥有的文件中

void warn() {}
void my_fun() { warn(); }

我编译它然后运行

$ R CMD SHLIB test.c
$ R -e "dyn.load('test.so'); .C('my_fun')"

使用我的 linux gcc 4.6.3 版,R 输出为

> dyn.load('test.so'); .C('my_fun')
R: Success
list()

来自warnlibc 中定义的函数的“R: Success”(参见man warnerr.h 中定义)。发生的情况是R理所当然地加载了几个动态库,然后按照指示加载了test.so。当调用 my_fun 时,动态链接器解析warn,但解析规则是全局搜索warn符号,而不仅仅是在 test.so 中。我真的不知道全局搜索规则是什么,也许是按照打开 .so 的顺序,但无论如何,分辨率都不是我所期望的。

什么是要做?指定

static void warn() {}

在编译时强制解析,当创建 .o 时,从而避免问题。warn例如,如果在一个文件 (utilities.c) 和另一个文件中定义,这将不起作用my_fun。在 Linux 上dlopen(用于加载共享对象的函数)可以提供一个标志RTLD_DEEPBIND,该标志在全局之前在本地进行符号解析,但是(a)R 不使用dlopen这种方式,并且(b)有几个(参见第 9 页)保留用这种方法。据我所知,最好的做法是尽可能使用static,并仔细命名函数以避免名称冲突。后者并不像看起来那么糟糕,因为 R 加载包共享对象,因此包符号本身不会添加到全局名称空间(参见?dyn.loadlocal论点,还请注意特定于操作系统的警告)。

我有兴趣听到更强大的“最佳实践”。

于 2012-06-21T19:23:38.200 回答