编译在Linux上同时使用R和numpy的C代码

zab*_*kar 8 c python numpy r

我有一些使用numpy和R的C代码.在Windows上,它使用MSVC编译为.dll,可以从R动态加载并传递所有测试.但是,我没能让它在Debian上运行.

为了研究这个问题,我创建了以下minimal-ish示例:

#include <Python.h>
#include <Rinternals.h>
#include <numpy/arrayobject.h>

SEXP main() {
   Py_Initialize();
   import_array();

   SEXP one = PROTECT(allocVector(INTSXP, 1));
   INTEGER(one)[0] = 1;
   npy_intp dims[1] = {1};
   int data[1] = {1};
   PyObject *another = PyArray_SimpleNewFromData(1, dims, NPY_INT, data);

   Rprintf("Hello, %d ", INTEGER(one)[0] + *(int*)PyArray_DATA(another));
   PyRun_SimpleString("print('worlds')");

   UNPROTECT(1);
   return one;
}
Run Code Online (Sandbox Code Playgroud)

我可以编译它

cl /I "C:\Program Files\R\R-3.2.0\include" /I "C:\Python34\include" /I "C:\Python34\Lib\site-packages\numpy\core\include" /c hello.c
link /dll /export:main hello.obj Rdll.lib C:/Python34/libs/python34.lib
Run Code Online (Sandbox Code Playgroud)

其中,Rdll.lib从创建%R_HOME%\bin\x64\R.dll

pexports R.dll > R.exp
link /lib /def:R.exp /machine:x64 /out:Rdll.lib
Run Code Online (Sandbox Code Playgroud)

然后它可以从R使用:

> dyn.load(paste0("hello", .Platform$dynlib.ext))
> .Call("main")
Hello, 2 worlds
[1] 1
Run Code Online (Sandbox Code Playgroud)

但是,当我在Debian上编译它时

gcc -shared -fPIC -I/usr/share/R/include -I/usr/include/python2.6 -lpython2.6 -L/usr/lib64/R/lib -lR hello.c -o hello.so
Run Code Online (Sandbox Code Playgroud)

并从R导入它发生以下情况:

> dyn.load("hello.so")
> .Call("main")
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.6/numpy/__init__.py", line 132, in <module>
    import add_newdocs
  File "/usr/lib/pymodules/python2.6/numpy/add_newdocs.py", line 9, in <module>
    from lib import add_newdoc
  File "/usr/lib/pymodules/python2.6/numpy/lib/__init__.py", line 4, in <module>
    from type_check import *
  File "/usr/lib/pymodules/python2.6/numpy/lib/type_check.py", line 8, in <module>
    import numpy.core.numeric as _nx
  File "/usr/lib/pymodules/python2.6/numpy/core/__init__.py", line 5, in <module>
    import multiarray
ImportError: /usr/lib/pymodules/python2.6/numpy/core/multiarray.so: undefined symbol: _Py_ZeroStruct

 *** caught segfault ***
address 0x4, cause 'memory not mapped'
Run Code Online (Sandbox Code Playgroud)

它会产生分段错误,除非与numpy相关的所有内容都被注释掉.与来自R的纯Python交互似乎没问题.但是一旦import_array()被召唤就会出现段错误.我-I/usr/share/pyshared/numpy/core/include/出于绝望而加入,并没有改变任何东西.

最后,如果我编译以下(类似于以前,但略有改动)的代码

#include <Python.h>
#include <Rinternals.h>
#include <numpy/arrayobject.h>

int main() {
   char *localArgs[] = {"R", "--silent"};
   Rf_initEmbeddedR(2, localArgs);
   Py_Initialize();
   import_array();

   SEXP one = PROTECT(allocVector(INTSXP, 1));
   INTEGER(one)[0] = 1;
   npy_intp dims[1] = {1};
   int data[1] = {1};
   PyObject *another = PyArray_SimpleNewFromData(1, dims, NPY_INT, data);

   Rprintf("Hello, %d ", INTEGER(one)[0] + *(int*)PyArray_DATA(another));
   PyRun_SimpleString("print('worlds')");

   UNPROTECT(1);
}
Run Code Online (Sandbox Code Playgroud)

在同一个Debian机器上

 gcc -I/usr/share/R/include -I/usr/include/python2.6 -lpython2.6 -L/usr/lib64/R/lib -lR hello.c -o hello
Run Code Online (Sandbox Code Playgroud)

并称之为

LD_LIBRARY_PATH=/usr/lib64/R/lib R_HOME=/usr/lib64/R ./hello
Run Code Online (Sandbox Code Playgroud)

它突然没有崩溃,运作良好并产生"你好,2个世界",正如预期的那样.

版本是:Windows:编译器版本19.00.23506 for x64,Python 3.4.4,numpy 1.9.3,R 3.2.0 Debian:gcc版本4.4.5(目标:x68_64-linux-gnu),Python 2.6.6, numpy 1.4.1,R 3.2.1

我究竟做错了什么?

更新:使用gcc和clang在Python 3.2和Python 2.7上测试Ubuntu.问题依然存在.

zab*_*kar 0

一个可行的解决方案(适用于本示例以及实际代码)是使用 dlopen 加载 libpythonX.Y.so:

 #include <Python.h>
 #include <Rinternals.h>
 #include <numpy/arrayobject.h>

 #ifndef _WIN32
 #include <dlfcn.h>
 #endif

 SEXP main() {
 #ifndef _WIN32
    dlopen("libpython2.6.so", RTLD_NOW | RTLD_GLOBAL);
 #endif
    Py_Initialize();
    import_array();

    SEXP one = PROTECT(allocVector(INTSXP, 1));
    INTEGER(one)[0] = 1;
    npy_intp dims[1] = {1};
    int data[1] = {1};
    PyObject *another = PyArray_SimpleNewFromData(1, dims, NPY_INT, data);

    Rprintf("Hello, %d ", INTEGER(one)[0] + *(int*)PyArray_DATA(another));
    PyRun_SimpleString("print('worlds')");

    UNPROTECT(1);
    return one;
}
Run Code Online (Sandbox Code Playgroud)

然而,我并不完全理解为什么需要它。