如何从python SWIG包装器中下载c ++对象?

时间:2010-10-13 06:34:56

标签: c++ python swig typeerror downcast

问题:我使用SWIG在python中包装了一些c ++代码。在python方面,我想采用一个包装的c ++指针并将其向下转换为指向子类的指针。我在SWIG .i文件中添加了一个新的c ++函数来执行此向下转换,但是当我从python调用它时,我得到一个TypeError。

以下是详细信息:

我有两个c ++类,Base和Derived。 Derived是Base的子类。我有一个第三类,Container,它包含一个Derived,并提供了一个访问器。访问器将Derived返回为const Base&,如图所示:

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

  private:
    Derived derived_;
};

我使用SWIG在python中包装了这些类。在我的python代码中,我想将Base引用向下转换为Derived。要做到这一点,我已经在swig .i文件中写了一个c ++中的辅助函数来执行向下转换:

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

在我的python代码中,我称之为向下转换函数:

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

当我这样做时,我收到以下错误:

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

为什么会发生这种情况?

作为参考,以下是SWIG生成的.cxx文件的相关位;即原始函数,以及它的python-interface-ified doppelganger:

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

非常感谢任何帮助。

- 马特

3 个答案:

答案 0 :(得分:3)

正如我上面评论的那样,这似乎适用于swig 1.3.40。

以下是我的文件:

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"

跑步:

$ 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

答案 1 :(得分:0)

我在你的代码中注意到的两件事第一个GetBase返回对const的引用,第二个是CastToDerived需要一个指向非const Base的指针。

即使在C ++中,你也有足够的麻烦来完成这项工作。我不知道还有什么应该是错的,但我会先尝试这个问题。

答案 2 :(得分:0)

您是否可能多次定义Base类?我在ctypes中遇到了类似的问题,我在两个不同的模块中无意中定义了相同的结构类。我在纯Python中也发生了类似的事情,我使用imp.load_module来加载插件类,创建该类的对象,然后重新加载模块 - 噗!创建的对象将不再通过类的isinstance测试,因为重新加载的类即使具有相同的名称,也是一个不同的类,具有不同的id。 (this blog entry中的更完整描述。)