【问题标题】:How do I down-cast a c++ object from a python SWIG wrapper?如何从 python SWIG 包装器向下转换 c++ 对象?
【发布时间】:2011-04-24 16:36:47
【问题描述】:

问题:我使用 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接口化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;
}

任何帮助将不胜感激。

-- 马特

【问题讨论】:

  • GetBase() 生成的代码是什么样的?该错误意味着传递给 CastToDerived 的对象不是正确的类型 - 它是什么类型?
  • 为了它的价值,我只是尝试基于上述创建一个示例,使用 swig 1.3.40,我没有收到此错误。
  • 太奇怪了……我试着写一些像你的例子一样的东西,克里斯,确实奏效了。正如您所料,我实际上遇到问题的代码不是玩具问题,我只是将其玩具化以使其足够短以适应问题。我当前的解决方法是在我的 c++ 库中定义 CastToDerived 函数,而不是在 .i 文件中的 %inline %{ ... %} 块中。这解决了问题。也许这个细节可能是对正确人的暗示?我仍然希望不将 SWIG 助手添加到我的 c++ 库中。
  • 我要做的是使用 -g 编译所有内容,不进行优化,然后单步执行代码以查看 SWIG_ConvertPtr 失败的地方,希望这能给我一个线索来了解正在发生的事情。跨度>

标签: c++ python swig typeerror downcast


【解决方案1】:

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

即使在 C++ 中,您也很难完成这项工作。我不知道还有什么问题,但我会先解决这个问题。

【讨论】:

  • 我认为这不是问题。正如您可能建议的那样,原始 CastToDerived 采用 const 引用,并返回一个 const 指针。即使在那时我也有同样的错误。 SWIG 获取所有引用并将它们转换为指针,并丢弃 constness。这就是为什么我的 CastToDerived 现在接受并返回非常量指针的原因。回复:在这里痛饮文档:swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_nn18 和这里:swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_const
【解决方案2】:

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

【讨论】:

    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 2021-08-29
      • 1970-01-01
      • 2011-07-22
      • 2015-12-15
      • 2021-05-23
      • 1970-01-01
      • 2011-05-01
      • 2022-01-15
      • 1970-01-01
      相关资源
      最近更新 更多