2015-08-12 1 views
6

Mam problemy z prawidłowym zainicjowaniem interfejsu NumPy C API. Wydaje mi się, że udało mi się wyizolować problem polegający na wywołaniu import_array z innej jednostki tłumaczeniowej, ale nie wiem, dlaczego to powinno mieć znaczenie.Segfault, gdy import_array nie znajduje się w tej samej jednostce tłumaczeniowej

minimalna przykład praca:

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

Polecenia
#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; 
} 

Kompilator:

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 

I zostały przetestowane z Clang 3.6.0 GCC 4.9.2, Python 2,7 Python 3.4 (z odpowiednio zmodyfikowaną wrap_import_array, ponieważ różni się od Pythonie 2 .x i 3.x). Różne kombinacje dają taki sam wynik: jeśli nie zadzwonię pod numer loc_initialize, program ulegnie uszkodzeniu w wywołaniu PyArray_DescrFromType. Mam wersję NumPy 1.8.2. Dla porównania uruchamiam to w Ubuntu 15.04.

Co mnie najbardziej zaskakuje, to this C++ NumPy wrapper wydaje się uciec dzwoniąc pod numer import_array w innej jednostce tłumaczeniowej.

Czego mi brakuje? Dlaczego muszę zadzwonić pod numer import_array z tej samej jednostki tłumaczeniowej, aby rzeczywiście zadziałała? Co ważniejsze, jak mogę go uruchomić, gdy zadzwonię pod numer import_array z innej jednostki tłumaczeniowej, takiej jak opakowanie Boost.NumPy?

Odpowiedz

4

Po wykopaniu przez nagłówkach NumPy, myślę, że znalazłem rozwiązanie:

w numpy/__multiarray_api.h, tam jest dział dotyczący gdzie wewnętrzny bufor API powinno być. Dla zwięzłości, oto odpowiedni fragment:

#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 

Wygląda na to ma na celu umożliwienie wielu modułów definiować własne wewnętrzny bufor API, w której każdy moduł musi wywołać własne import_array zdefiniowania.

spójny sposób, aby uzyskać kilka jednostek tłumaczenie używać tego samego buforu API wewnętrzna jest w każdym module, określenie PY_ARRAY_UNIQUE_SYMBOL do jakiejś biblioteki unikalną nazwę, a następnie każdą jednostkę tłumaczeniową inny niż ten, w którym owijka import_array definiuje definiuje NO_IMPORT lub NO_IMPORT_ARRAY. Nawiasem mówiąc, istnieją podobne makra dla funkcji ufunc: PY_UFUNC_UNIQUE_SYMBOL i NO_IMPORT/NO_IMPORT_UFUNC.

Zmodyfikowany przykład praca:

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 

plik1.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; 
} 

Nie wiem jakich pułapek są z tego włamania lub czy są jakieś lepsze alternatywy, ale to wydaje się co najmniej skompilować i uruchomić bez żadnych uszkodzeń.

+0

Czy możesz podzielić się definicją MYLIBRARY_ARRAY_API? Dzięki. – ssk

+0

MYLIBRARY_ARRAY_API to tylko przykładowa nazwa używana do przechowywania wewnętrznych buforów NumPy. Nie jest to makro, ale powinno być unikalne dla twojej aplikacji/biblioteki, aby zapobiec konfliktowi z innymi bibliotekami. – helloworld922