我在使NumPy C API正确初始化时遇到问题。我想我已经将问题从一个不同的翻译单位调用import_array
,但我不知道为什么这个问题很重要。
最小的工作示例:
header1.hpp
#ifndef HEADER1_HPP
#define HEADER1_HPP
#include <Python.h>
#include <numpy/npy_3kcompat.h>
#include <numpy/arrayobject.h>
void initialize();
#endif
file1.cpp
#include "header1.hpp"
void* wrap_import_array()
{
import_array();
return (void*) 1;
}
void initialize()
{
wrap_import_array();
}
file2.cpp
#include "header1.hpp"
#include <iostream>
void* loc_wrap_import_array()
{
import_array();
return (void*) 1;
}
void loc_initialize()
{
loc_wrap_import_array();
}
int main()
{
Py_Initialize();
#ifdef USE_LOC_INIT
loc_initialize();
#else
initialize();
#endif
npy_intp dims[] = {5};
std::cout << "creating descr" << std::endl;
PyArray_Descr* dtype = PyArray_DescrFromType(NPY_FLOAT64);
std::cout << "zeros" << std::endl;
PyArray_Zeros(1, dims, dtype, 0);
std::cout << "cleanup" << std::endl;
return 0;
}
编译器命令:
g++ file1.cpp file2.cpp -o segissue -lpython3.4m -I/usr/include/python3.4m -DUSE_LOC_INIT
./segissue
# runs fine
g++ file1.cpp file2.cpp -o segissue -lpython3.4m -I/usr/include/python3.4m
./segissue
# segfaults
我已经使用Clang 3.6.0,GCC 4.9.2,Python 2.7和Python 3.4(经过适当修改的wrap_import_array
测试了这一点,因为这在Python 2.x和3.x之间是不同的)。各种组合都会产生相同的结果:如果我不调用loc_initialize
,程序将在PyArray_DescrFromType
调用中进行段错误。我有NumPy版本1.8.2。作为参考,我在Ubuntu 15.04中运行它。
最让我感到困惑的是,this C++ NumPy wrapper似乎可以在另一个翻译单元中调用import_array
。
我错过了什么?为什么我必须从同一个翻译单元拨打import_array
才能使其真正生效?更重要的是,当我从Boost.NumPy包装器等其他翻译单元调用import_array
时,如何让它工作?
答案 0 :(得分:5)
在深入了解NumPy标题后,我想我找到了一个解决方案:
在numpy/__multiarray_api.h
中的,有一节介绍内部API缓冲区应该在哪里。为简明扼要,这是相关的片段:
#if defined(PY_ARRAY_UNIQUE_SYMBOL)
#define PyArray_API PY_ARRAY_UNIQUE_SYMBOL
#endif
#if defined(NO_IMPORT) || defined(NO_IMPORT_ARRAY)
extern void **PyArray_API;
#else
#if defined(PY_ARRAY_UNIQUE_SYMBOL)
void **PyArray_API;
#else
static void **PyArray_API=NULL;
#endif
#endif
看起来这是为了让多个模块定义自己的内部API缓冲区,其中每个模块都必须调用自己的import_array
定义。
让每个模块使用相同的内部API缓冲区的一致方法是在每个模块中,将PY_ARRAY_UNIQUE_SYMBOL
定义为某个库唯一名称,然后将每个翻译单元定义为import_array包装器之外的其他翻译单元已定义定义NO_IMPORT
或NO_IMPORT_ARRAY
。顺便提一下,ufunc功能有类似的宏:PY_UFUNC_UNIQUE_SYMBOL
和NO_IMPORT
/ NO_IMPORT_UFUNC
。
修改后的工作示例:
<强> header1.hpp 强>
#ifndef HEADER1_HPP
#define HEADER1_HPP
#ifndef MYLIBRARY_USE_IMPORT
#define NO_IMPORT
#endif
#define PY_ARRAY_UNIQUE_SYMBOL MYLIBRARY_ARRAY_API
#define PY_UFUNC_UNIQUE_SYMBOL MYLIBRARY_UFUNC_API
#include <Python.h>
#include <numpy/npy_3kcompat.h>
#include <numpy/arrayobject.h>
void initialize();
#endif
<强> file1.cpp 强>
#define MYLIBRARY_USE_IMPORT
#include "header1.hpp"
void* wrap_import_array()
{
import_array();
return (void*) 1;
}
void initialize()
{
wrap_import_array();
}
<强> file2.cpp 强>
#include "header1.hpp"
#include <iostream>
int main()
{
Py_Initialize();
initialize();
npy_intp dims[] = {5};
std::cout << "creating descr" << std::endl;
PyArray_Descr* dtype = PyArray_DescrFromType(NPY_FLOAT64);
std::cout << "zeros" << std::endl;
PyArray_Zeros(1, dims, dtype, 0);
std::cout << "cleanup" << std::endl;
return 0;
}
我不知道这个黑客有什么陷阱,或者有更好的替代方案,但这似乎至少可以在没有任何段错误的情况下编译和运行。