2012-02-14 17 views
75

SWIG ile güzelce çalışan küçük bir projem var. Özellikle, bazı işlevlerim Python'da tuplelere çevrilen std::vector s döndürüyor. Şimdi, çok sayıda sayısal yapıyorum, bu yüzden sadece SWIG bunları C++ kodundan döndükten sonra numpy dizilerine dönüştürüyor. Bunu yapmak için, SWIG'de aşağıdaki gibi bir şey kullanırım.SWIG'in yeni yerleşik özelliği ile pythonappend'i kullanmanın bir yolu var mı?

%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %} 

(Aslında val aslında bir tanımlama grubu olup olmadığını kontrol neden olan, yüzen dönüş bazıları Veri olarak adlandırılan bir kaç fonksiyonları vardır.) Bu sadece güzel çalışıyor.

Ayrıca, şu anda mevcut olan -builtin bayrağını kullanmak istiyorum. Bu Veri işlevlerine yapılan çağrılar nadirdir ve çoğunlukla etkileşimlidir, bu nedenle yavaşlıkları bir sorun değildir, ancak yerleşik seçenekle önemli ölçüde hızlanan başka yavaş döngüler de vardır.

Sorun şu ki, bu bayrağı kullandığımda, pythonappend özelliği sessizce yok sayılır. Şimdi, Data tekrar bir tuple döndürüyor. Uyuşmuş dizileri geri döndürmenin bir yolu var mı? Tipemaps kullanmayı denedim, ama dev bir karmaşaya dönüştü.

Düzenleme: Borealid çok güzel soruya cevap vardır

. Sadece tamlık için, ihtiyacım olan bir çift ile ilgili ama çok farklı tiplemeler ekledim çünkü const referansı ile dönüyorum ve vektörlerin vektörlerini kullanıyorum (başlama!). Bunlar, kimsenin küçük farklılıkları anlamaya çalışmasını istemeyecek kadar farklı.

%typemap(out) std::vector<int>& { 
    npy_intp result_size = $1->size(); 
    npy_intp dims[1] = { result_size }; 
    PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT); 
    int* dat = (int*) PyArray_DATA(npy_arr); 
    for (size_t i = 0; i < result_size; ++i) { dat[i] = (*$1)[i]; } 
    $result = PyArray_Return(npy_arr); 
} 
%typemap(out) std::vector<std::vector<int> >& { 
    npy_intp result_size = $1->size(); 
    npy_intp result_size2 = (result_size>0 ? (*$1)[0].size() : 0); 
    npy_intp dims[2] = { result_size, result_size2 }; 
    PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_INT); 
    int* dat = (int*) PyArray_DATA(npy_arr); 
    for (size_t i = 0; i < result_size; ++i) { for (size_t j = 0; j < result_size2; ++j) { dat[i*result_size2+j] = (*$1)[i][j]; } } 
    $result = PyArray_Return(npy_arr); 
} 

Düzenleme 2:

Ben benzer sorunlar Monk yaklaşımı (explained here) @ kullanılarak çözülebilir, aradığı oldukça ne rağmen.

+4

Bunu, tipik bir şekilde pythonappend'in normal olarak yerleştirildiği kodu kaldırdığı için, bir yazım haritası yazmadan ve C tarafında yapmadan yapabileceğinizi sanmıyorum. -builtin'in çok daha hızlı olduğundan emin misiniz (yani, profil kullanmanız sizi yönlendirdi mi?). İki modülü kullanmak için cazip olmalıyım. – Flexo

+0

'-builtin' pythonappend'i göz ardı ettiğine dair bir uyarı olmadığına şaşırdım. Ben std :: vector's numm dizileri içine typemapping meydan okuma için değilim. Profil yaptım ve arayüzümdeki en sinir bozucu döngüyü önemli ölçüde hızlandırdı (ara vermek için yeterince uzun değil; sık beklemek için çok uzun). Ama aynı zamanda bu döngüyü C++ koduma taşıyabildiğimi fark ettim - biraz garip olsa da. Yani ben böyle giderim. Yine de, 'iki modül' öneriniz ilginç ve diğer durumlarda yararlı olabilir. – Mike

+0

SWIG ile -Wall'ı aradınız mı? Bu davada uyarılacağını düşündüm. – Flexo

cevap

6

typemap kullanarak biraz dağınık olsun, ancak bu görevi yerine getirmenin doğru yolu olduğunu kabul ediyorum. Ayrıca yudum belgeleri doğrudan %pythonappend-builtin ile uyumsuz olduğunu söylemek etmediğini doğru, ama kuvvetle ima: %pythonappendPython vekil sınıfına ekler, ve Python vekil sınıf -builtin ile birlikte hiç biri yok bayrağı.

önce, yudum Python küpe içine C++ std::vector nesneleri dönüştürmek zorunda ve sonra numpy geri aşağı olanlar dizilerini geçiyordu ne yaptıklarını - yine dönüştürülmüştür.

Gerçekten yapmak istediğiniz şey, bunları C düzeyinde bir defaya dönüştürmektir.

%{ 
#include "numpy/arrayobject.h" 
%} 

%init %{ 
    import_array(); 
%} 

%typemap(out) std::vector<int> { 
    npy_intp result_size = $1.size(); 

    npy_intp dims[1] = { result_size }; 

    PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT); 
    int* dat = (int*) PyArray_DATA(npy_arr); 

    for (size_t i = 0; i < result_size; ++i) { 
     dat[i] = $1[i]; 
    } 

    $result = PyArray_Return(npy_arr); 
} 

Bu yapı ve bir dizi geri C düzeyinde numpy işlevlerini kullanır:

Burada NumPy tamsayı diziler her std::vector<int> nesneleri dönecek bir kod.amacıyla,:

  • Pithon yüklendiğinde
  • nedenleri import_array herhangi
  • Maps (aksi takdirde tüm NumPy yöntemleri segfault olacaktır) çağrılacak numpy en arrayobject.h dosya C++ çıktı dosyasına dahil edilir sağlar bir typemap

ile NumPy diziler içine std::vector<int> getirileri Bu kod başlıkları wh size önce %importyerleştirilmelidir ich, std::vector<int> döndüren işlevleri içerir. Bu kısıtlamadan başka, tamamen kendi kendine yeten, bu yüzden kod tabanına çok fazla öznel "karmaşa" eklememelidir.

Diğer vektör türlerine ihtiyacınız varsa, NPY_INT ve tüm int* ve int bitlerini değiştirebilirsiniz, aksi halde yukarıdaki işlevi çoğaltabilirsiniz.

+0

Mükemmel! Tipik harita için sahip olduğun tüm öğeleri aldım ama onları düzgün bir şekilde bir araya getirmemiştim. Bu projeyle henüz resmi olarak çalışmadığım halde, daha basit bir modül oluşturarak oldukça kapsamlı bir test yaptım. Çok teşekkürler! – Mike

İlgili konular