2010-10-13 34 views
5

Vấn đề: Tôi đã gói một số mã C++ trong python bằng SWIG. Về phía trăn, tôi muốn lấy một con trỏ C++ được bọc và đặt nó xuống thành một con trỏ tới một lớp con. Tôi đã thêm một hàm C++ mới vào tệp SWIG .i thực hiện thao tác này, nhưng khi tôi gọi nó từ python, tôi nhận được một TypeError.Làm cách nào để bỏ qua đối tượng C++ từ trình bao bọc SWIG trăn?

Dưới đây là các chi tiết:

Tôi có hai lớp C++, Cơ sở và Nguồn gốc. Có nguồn gốc là một phân lớp của Base. Tôi có một lớp thứ ba, Container, chứa một Derived, và cung cấp một accessor cho nó. Accessor trả về Derived dưới dạng const Base &, như được hiển thị:

class Container { 
    public: 
    const Base& GetBase() const { 
     return derived_; 
    } 

    private: 
    Derived derived_; 
}; 

Tôi đã bọc các lớp này vào python bằng SWIG. Trong mã python của tôi, tôi muốn down tham chiếu Base trở lại một Derived. Để làm điều này, tôi đã viết vào uống một lân .i nộp một hàm helper trong C++ mà không xuống-đúc:

%inline %{ 
    Derived* CastToDerived(Base* base) { 
    return static_cast<Derived*>(base); 
    } 
%} 

Trong mã python của tôi, tôi gọi chức năng này xuống đúc:

base = container.GetBase() 
derived = CastToDerived(base) 

Khi tôi làm như vậy, tôi nhận được lỗi sau:

TypeError: in method 'CastToDerived', argument 1 of type 'Base *' 

Tại sao có thể này xảy ra?

Để tham khảo, dưới đây là các bit liên quan của tệp .cxx được tạo bởi SWIG; cụ thể là chức năng gốc và giao diện python-ified doppelganger của nó:

Derived* CastToDerived(Base* base) { 
    return static_cast<Derived*>(base); 
    } 

// (lots of other generated code omitted) 

SWIGINTERN PyObject *_wrap_CastToDerived(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { 
    PyObject *resultobj = 0; 
    Base *arg1 = (Base *) 0 ; 
    void *argp1 = 0 ; 
    int res1 = 0 ; 
    PyObject * obj0 = 0 ; 
    Derived *result = 0 ; 

    if (!PyArg_ParseTuple(args,(char *)"O:CastToDerived",&obj0)) SWIG_fail; 
    res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Base, 0 | 0); 
    if (!SWIG_IsOK(res1)) { 
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "CastToDerived" "', argument " "1"" of type '" "Base *""'"); 
    } 
    arg1 = reinterpret_cast< Base * >(argp1); 
    result = (Derived *)CastToDerived(arg1); 
    resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Derived, 0 | 0); 
    return resultobj; 
fail: 
    return NULL; 
} 

Bất kỳ trợ giúp nào cũng sẽ được đánh giá cao.

- Matt

+0

gì mã được tạo cho GetBase() như thế nào? Lỗi có nghĩa là đối tượng được truyền vào CastToDerived là loại không đúng - loại đó là gì? –

+0

Đối với những gì nó có giá trị, tôi chỉ cố gắng tạo ra một ví dụ dựa trên ở trên, sử dụng swig 1.3.40, và tôi đã không nhận được lỗi này. –

+0

Lạ thật ... Tôi đã thử viết một cái gì đó giống như ví dụ của bạn, Chris, và thực sự nó đã hoạt động. Như bạn có thể mong đợi, mã tôi thực sự gặp rắc rối với không phải là một số vấn đề đồ chơi, tôi chỉ đồ chơi-ified nó để làm cho nó đủ ngắn để phù hợp trong một câu hỏi. Cách khắc phục hiện tại của tôi là xác định hàm CastToDerived trong thư viện C++ của tôi, thay vì trong khối% inline% {...%} trong tệp .i. Điều đó khắc phục được sự cố. Có lẽ chi tiết đó có thể là một gợi ý cho đúng người? Tôi vẫn muốn tôi có thể làm mà không cần thêm SWIG helpers vào thư viện C++ của tôi. – SuperElectric

Trả lời

3

Như tôi đã nhận xét ở trên, điều này dường như hoạt động tốt với swig 1.3.40.

Dưới đây là tác phẩm của tôi:

c.h:

#include <iostream> 
class Base {}; 
class Derived : public Base 
{ 
    public: 
     void f() const { std::cout << "In Derived::f()" << std::endl; } 
}; 
class Container { 
    public: 
    const Base& GetBase() const { 
     return derived_; 
    } 
    private: 
    Derived derived_; 
}; 

c.i

%module c 

%{ 
#define SWIG_FILE_WITH_INIT 
#include "c.h" 
%} 

%inline %{ 
    Derived* CastToDerived(Base* base) { 
    return static_cast<Derived*>(base); 
    } 
%} 
class Base 
{ 
}; 

class Derived : public Base 
{ 
    public: 
     void f() const; 
}; 

class Container { 
    public: 
    const Base& GetBase() const; 
}; 

ctest.py

import c 

container = c.Container() 
b = container.GetBase() 
d = c.CastToDerived(b) 
d.f() 
print "ok" 

Một chạy:

$ swig -c++ -python c.i 
$ g++ -fPIC -I/usr/include/python2.6 -c -g c_wrap.cxx 
$ g++ -shared -o _c.so c_wrap.o 
$ python ctest.py 
In Derived::f() 
ok 
0

2 điều tôi nhận thấy trong mã của bạn 1 GetBase trả về một tham chiếu đến const và thứ hai mà CastToDerived hy vọng một con trỏ đến cơ sở không const.

Ngay cả trong C++, bạn vẫn gặp khó khăn khi thực hiện tác phẩm này. Tôi không thể nói những gì khác nên sai nhưng tôi sẽ cố gắng để có được điều này fxied đầu tiên.

+0

Đó không phải là vấn đề, tôi nghĩ vậy. Bản gốc CastToDerived lấy tham chiếu const và trả về một con trỏ const, như bạn có thể đề xuất. Tôi đã có lỗi tương tự ngay cả sau đó. SWIG lấy tất cả các tham chiếu và biến chúng thành con trỏ, và giảm độ chói. Đây là lý do tại sao CastToDerived của tôi bây giờ có và trả về con trỏ không const. Re: swig docs tại đây: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_nn18 và tại đây: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_const – SuperElectric

0

Có thể bạn đang xác định lớp Cơ sở nhiều lần không? Tôi đã có vấn đề tương tự với ctypes nơi tôi vô tình định nghĩa cùng một lớp cấu trúc trong hai mô-đun khác nhau. Tôi cũng đã có một cái gì đó tương tự xảy ra trong Python tinh khiết, nơi tôi đã sử dụng imp.load_module để tải một lớp plugin, tạo ra một đối tượng của lớp đó, sau đó nạp lại module - poof! đối tượng đã tạo sẽ không còn vượt qua bài kiểm tra isinstance của lớp, vì lớp được nạp lại, mặc dù nó có cùng tên, là một lớp khác, với id khác. (Mô tả đầy đủ hơn trong this blog entry.)

Các vấn đề liên quan