本文为李你干嘛原创,转载请注明出处:Pybind11绑定C++抽象类(DLL接口)文章来源地址https://www.toymoban.com/news/detail-680439.html
摘要
假设我们将DLL中的接口封装成了C++抽象类,并将该类和DLL文件提供给用户,类似于抽象类导出DLL中描述的办法,如果这个时候我们想使用pybind11绑定这个C++抽象类,会遇到报错,如抽象类无法实例化等等,此时Pybind11给出了辅助类的办法overriding-virtual-functions-in-python,但是如果只想转换C++抽象类的一部分的话,这个方案是不适用的。Pybind11有个很强大的功能,如果我们将C++类使用py::class绑定后,那么C++暴露给python的这个类会自动转换成Python的类。如果我们要大量的在Python中使用到这个C++抽象类且接触不到其基类时,就没有办法完成这个抽象类的绑定。
在这里我们给出一个解决思路,即用Wrapper类将C++的抽象类封装,并对这个类使用pybind绑定,这样我们就有了一个Python端的Wrapper类。再根据官网给出的办法Custom Type Casters实现从Python端Wrapper类到C++抽象类和从C++抽象类到Python端Wrapper类的自动转换。这样当C++暴露给Python这个抽象类时,pybind会自动调用转换器将抽象类转换成Wrapper类的Python对象,当Python的Wrapper类传递给C++时,会将Wrapper类变成C++抽象类。
问题描述
假设我们将C++抽象类AbstractDLLInterface作为DLL接口,ConcreteDLLInterface1类作为具体实现,但是并不把它暴露给用户。
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// 抽象类接口
class MYDLL_API AbstractDLLInterface {
public:
virtual ~AbstractDLLInterface() {}
virtual void dllFunction() = 0;
virtual AbstractDLLInterface* createInstance() const = 0;
};
// 具体的实现类1
class ConcreteDLLInterface1 : public AbstractDLLInterface {
public:
void dllFunction() override;
AbstractDLLInterface* createInstance() const override;
};
// 在实现文件中提供具体实现
void ConcreteDLLInterface1::dllFunction() {
// DLL 接口函数的具体实现
// ...
}
AbstractDLLInterface* ConcreteDLLInterface1::createInstance() const {
return new ConcreteDLLInterface1();
}
现在我们只有AbstractDLLInterface类的声明和一个DLL文件,我们的C++代码中需要经常使用AbstractDLLInterface类作为返回值或者函数参数,而我们需要把这一部分用Pybind绑定。下面给出解决方案。
解决方案
创建Wrapper类
class Wrapper {
public:
Wrapper(AbstractDLLInterface* instance) : instance_(instance) {}
void dllFunction() {
instance_->dllFunction();
}
AbstractDLLInterface* instance_;
};
定义AbstractDLLInterface类的type_caster
namespace PYBIND11_NAMESPACE {
namespace detail {
template <> struct type_caster<AbstractDLLInterface> {
public:
PYBIND11_TYPE_CASTER(AbstractDLLInterface, const_name("AbstractDLLInterface"));
/**
* Conversion part 1 (Python -> C++): convert a PyObject into an AbstractDLLInterface
*/
bool load(handle src, bool) {
Wrapper wrapper = py::cast<Wrapper>(src);
value = *(wrapper.instance_);
return true;
}
/**
* Conversion part 2 (C++ -> Python): convert an AbstractDLLInterface into a PyObject
*/
static handle cast(AbstractDLLInterface src, return_value_policy policy, handle parent) {
std::shared_ptr<Wrapper> wrapper_ptr = std::make_shared<Wrapper>(&src);
return type_caster<std::shared_ptr<Wrapper>>::cast(wrapper_ptr, py::return_value_policy::take_ownership, parent);
}
};
}
} // namespace PYBIND11_NAMESPACE::detail
Pybind中的处理思路无非是在绑定好的类、函数上,在python遇到定义过的类或者类型等就将其从C++的类包装成python的类,在python端有参数要传递给C++就将参数从Python类转换成C++类。上面的代码就实现了这个过程,我们将Python中的Wrapper类与C++中的AbstractDLLInterface类视为等效的,那么如果有Python中的Wrapper类需要传入到C++中时,会调用load将AbstractDLLInterface类实例从Wrapper类中提取出来,如果C++中有AbstractDLLInterface类实例要传入到Python中时,会调用cast新建一个Wrapper实例,再将这个Wrapper实例转换成Python对象传入到Python空间中。
实际操作用大多数不会用到AbstractDLLInterface而是AbstractDLLInterface的智能指针,此处是对AbstractDLLInterface进行转换,但是处于安全性考虑最好对std::shared_ptr<AbstractDLLInterface>类型进行转换。
绑定Pybind
// 绑定代码
PYBIND11_MODULE(my_module, m) {
py::class_<Wrapper, std::shared_ptr<Wrapper>>(m, "Wrapper")
.def(py::init<AbstractDLLInterface*>())
.def("dllFunction", &Wrapper::dllFunction);
}
注意此时我们不需要绑定AbstractDLLInterface类,绑定AbstractDLLInterface类编译时会报错。py::class_传入参数Wrapper, std::shared_ptr<Wrapper>可以保证Python可以识别Wrapper和Wrapper的指针。文章来源:https://www.toymoban.com/news/detail-680439.html
本文为李你干嘛原创,转载请注明出处:Pybind11绑定C++抽象类(DLL接口)
到了这里,关于Pybind11绑定C++抽象类(DLL接口)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!