从基础*到衍生*的垂涎三尺

Hom*_*mar 2 c++ python swig downcast

我有以下使用SWIG暴露给Python的c ++类(简化):

struct Component
{
    virtual void update();
}

struct DerivedComponent : public Component
{
    void update() { cout << "DerivedComponent::update()" << endl; }
    void speak() { cout << "DerivedComponent::speak()" << endl; }
}

class Entity
{
public:
    Component* component(const std::string& class_name)
    {
        return m_components[class_name];
    }

private:
    std::unordered_map<std::string, Component*> m_components;
}
Run Code Online (Sandbox Code Playgroud)

现在,在Python中,我可以成功调用component("DerivedComponent").update()Entity实例。但是,component("DerivedComponent").speak()由于返回的类型component("DerivedComponent")报告为,因此我无法调用<class 'module.Component'>

component()为了调用中定义的方法,我显然需要转换函数的结果DerivedComponent。我曾希望Swig能像我相信Boost.Python一样执行自动向下转换。

缺少在c ++中定义一堆类型转换函数并将其公开给Python的方法,是否有使用Swig或Python进行向下转换的更好的解决方案?我有什么选择?

Fle*_*exo 5

您只需做一些工作,就可以完全使用Python完成您想要的事情。它可以按您希望的那样工作,因为在Python中,向下转换是没有意义的,因为函数的返回类型(或一般类型)不是强类型的,因此我们可以修改Entity::component函数以始终返回最派生的类型,无论它是什么。

要使其与C ++ / Python绑定一起使用,您需要为编写一个“输出”类型映射Entity::component。我已经写了一个示例,说明它可能如何工作。在这种情况下,我们必须略微缩小一下,因为知道向下转换的唯一方法是函数的参数。(例如,如果您的基类具有将其作为字符串/枚举返回的方法,则可以进一步简化它,而不必依赖于输入参数)。

%module test

%{
#include "test.hh"
%}

%include <std_string.i>

%typemap(out) Component * Entity::component {
    const std::string lookup_typename = *arg2 + " *";
    swig_type_info * const outtype = SWIG_TypeQuery(lookup_typename.c_str());
    $result = SWIG_NewPointerObj(SWIG_as_voidptr($1), outtype, $owner);
}

%include "test.hh"
Run Code Online (Sandbox Code Playgroud)

这使用该SWIG_TypeQuery函数要求Python运行时基于arg2查找类型(在您的示例中为字符串)。

我必须对您的示例标头(在本示例中名为test.hh)进行一些更改,以解决一些问题,然后才能将其制作成可以正常运行的演示,最终看起来像:

#include <iostream>
#include <map>
#include <string>

struct Component
{
    virtual void update() = 0;
    virtual ~Component() {}
};

struct DerivedComponent : public Component
{
    void update() { std::cout << "DerivedComponent::update()" << std::endl; }
    void speak() { std::cout << "DerivedComponent::speak()" << std::endl; }
};

class Entity
{
public:
    Entity() {
       m_components["DerivedComponent"] = new DerivedComponent;
    }

    Component* component(const std::string& class_name)
    {
        return m_components[class_name];
    }

private:
    std::map<std::string, Component*> m_components;
};
Run Code Online (Sandbox Code Playgroud)

然后,我用以下命令构建它:

swig -py3 -c++ -python -Wall test.i
g++ -Wall -Wextra test_wrap.cxx -I/usr/include/python3.4/ -lpython3.4m -shared -o _test.so
Run Code Online (Sandbox Code Playgroud)

有了这个,我就可以运行以下Python:

from test import *

e=Entity()
print(e)

c=e.component("DerivedComponent")
print(c)
print(type(c))

c.update()
c.speak()
Run Code Online (Sandbox Code Playgroud)

如您所愿,它可以工作:

from test import *

e=Entity()
print(e)

c=e.component("DerivedComponent")
print(c)
print(type(c))

c.update()
c.speak()
Run Code Online (Sandbox Code Playgroud)