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 コード内の任意の場所を呼び出すと、segfault が発生します。

  *** 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)

言うまでもなく、R 関数からではなく C または C++ ラッパーから同じ関数を呼び出した場合、コードは正常に実行されます。名前を変更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')"

私のLinuxgccバージョン4.6.3では、R出力は

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

その「R:Success」warnはlibcで定義された関数から来ています(man warnerr.hで定義されたを参照)。当然のことながら、Rはいくつかのダイナミックライブラリをロードし、指示に従ってtest.soをロードします。my_funが呼び出されると、ダイナミックリンカは解決warnしますが、解決のルールは、test.soだけでなく、シンボルをグローバルに検索することです。warnおそらく.soが開かれた順序で、グローバル検索ルールが何であるかは本当にわかりませんが、どのような場合でも、解像度は私が期待していた場所ではありません。

やるべきこと?指定する

static void warn() {}

.oが作成されるコンパイル時に強制的に解決されるため、問題が回避されます。warnたとえば、あるファイル(utilities.c)と別のファイルで定義されている場合、これは機能しませんmy_fun。Linux dlopen(共有オブジェクトのロードに使用される関数)では、グローバルに前にローカルでシンボル解決を行うフラグを提供できますがRTLD_DEEPBIND、(a)Rはdlopenその方法を使用せず、(b)いくつかの(p。9を参照)予約がありますこの種のアプローチで。私が知る限り、ベストプラクティスはstatic、可能な限り使用し、名前の競合を避けるために関数に注意深く名前を付けることです。Rは、パッケージシンボル自体がグローバル名前空間に追加されないようにパッケージ共有オブジェクトをロードするため、この後者は見た目ほど悪くはありません(を参照)。?dyn.loadおよびlocal引数、およびOS固有の警告にも注意してください)。

より堅牢な「ベストプラクティス」を聞いてみたいと思います。

于 2012-06-21T19:23:38.200 に答える