从基础*向下摇摆到派生*
Swig downcasting from Base* to Derived*
我有以下 c++ 类(简化(,我使用 SWIG 向 Python 公开
: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;
}
现在,在 Python 中,我可以在实体实例上成功调用component("DerivedComponent").update()
。但是,我无法调用component("DerivedComponent").speak()
,因为component("DerivedComponent")
返回的类型被报告为<class 'module.Component'>
。
我显然需要向下转换component()
函数的结果才能调用DerivedComponent
中定义的方法。我曾希望Swig能够像我相信Boost.Python那样执行自动下投。
除了在 c++ 中定义一大堆类型转换函数并将它们公开给 Python 之外,有没有更好的解决方案来使用 Swig 或 Python 进行向下转换?我有哪些选择?
你可以在Python中做你想做的事,只需要一点点工作。它的工作方式如您所愿,因为在 Python 中,向下转换毫无意义,因为函数的返回类型(或一般类型(不是强类型的,因此我们可以修改您的Entity::component
函数以始终返回派生最多的类型,无论它是什么。
要使其适用于您的 C++/Python 绑定,您需要为 Entity::component
编写一个"out"类型映射。我已经写了一个它如何工作的例子。在这种情况下,我们必须稍微改变一下,因为知道将其向下抛弃的唯一方法来自函数的参数。(例如,如果您的基类有一个将其作为字符串/枚举返回的方法,则可以进一步简化它,而不依赖于输入参数(。
%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"
这使用 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;
};
然后我用:
swig -py3 -c++ -python -Wall test.i
g++ -Wall -Wextra test_wrap.cxx -I/usr/include/python3.4/ -lpython3.4m -shared -o _test.so
有了这个,我就可以运行以下Python:
from test import *
e=Entity()
print(e)
c=e.component("DerivedComponent")
print(c)
print(type(c))
c.update()
c.speak()
这如您所希望的那样工作:
<test.Entity; proxy of <Swig Object of type 'Entity *' at 0xb7230458> >
Name is: DerivedComponent *, type is: 0xb77661d8
<test.DerivedComponent; proxy of <Swig Object of type 'DerivedComponent *' at 0xb72575d8> >
<class 'test.DerivedComponent'>
DerivedComponent::update()
DerivedComponent::speak()
我想做类似的事情,并根据这个问题提出了一个类似但不同的解决方案。
如果您提前知道可能的类型并且不介意额外的开销,则可以让"out"类型图循环并dynamic_cast到每个类型,以自动返回对象及其真实类型。SWIG 已经为具有 %factory 功能的指针实现了这一点:
%factory(Component* /* or add method name. this is just the typemap filter */,
DerivedComponent1,
DerivedComponent2);
查看 factory.swg 和 boost_shared_ptr.i,我也得到了这个shared_ptr和dynamic_pointer_cast的工作:
%define %_shared_factory_dispatch(Type)
if (!dcast) {
SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<Type> dobj
= SWIG_SHARED_PTR_QNAMESPACE::dynamic_pointer_cast<Type>($1);
if (dobj) {
dcast = 1;
SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<Type> *smartresult
= dobj ? new SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<Type>(dobj) : 0;
%set_output(SWIG_NewPointerObj(%as_voidptr(smartresult),
$descriptor(SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<Type> *),
SWIG_POINTER_OWN));
}
}%enddef
%define %shared_factory(BaseType,Types...)
%typemap(out) SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<BaseType> {
int dcast = 0;
%formacro(%_shared_factory_dispatch, Types)
if (!dcast) {
SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<BaseType> *smartresult
= $1 ? new SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<BaseType>($1) : 0;
%set_output(SWIG_NewPointerObj(%as_voidptr(smartresult),
$descriptor(SWIG_SHARED_PTR_QNAMESPACE::shared_ptr<BaseType> *),
SWIG_POINTER_OWN));
}
}%enddef
// Apply dynamic_pointer cast to all returned shared_ptrs of this type
%factory(Component /* must be a type for shared_ptr */,
DerivedComponent1,
DerivedComponent2);
- 为什么使用 "this" 指针调用派生成员函数?
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 在派生函数中指定void*参数
- 如何通过派生类函数更改基类中的向量
- 如何委托派生类使用其父构造函数?
- 如何使用单独文件中的派生类访问友元函数对象
- 派生类销毁的最佳实践是什么
- 如何使用基类指针引用派生类成员
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如何引用基类的派生类?
- 存储模板类型以强制转换回派生<T>
- 需要从 istream 和 ostream 派生 iostream
- 在 C++ 中用派生类型重写成员函数
- 具有多个类、派生类的C++正向声明
- 有没有一种"cleaner"的方法可以在指向基的指针向量中找到派生类的第一个实例?
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 如果基类包含双指针成员,则派生类的构造函数
- 为什么此派生对象无法访问基类的后递减方法?
- 从基础*向下摇摆到派生*