pybind11 - 用于 STL-vector-like 类的健壮类型脚轮
问题描述
我有一个与 STL-vector 非常相似的类(差异对于 pybind11 类型的 caster 并不重要,因此我将在这里忽略它们)。我为这个类写了一个类型转换。下面给出了我的代码的最小工作示例。显示问题的示例包含在代码下方。
问题是我的脚轮很有限(因为我用过py::array_t
)。原则上,该接口确实接受元组、列表和 numpy 数组。但是,当我基于 typename 重载时,输入的元组和列表的接口失败(即使它是不正确的类型,也只是选择了第一个重载)。
我的问题是:如何使类型脚轮更健壮?是否有一种有效的方法可以将尽可能多的现有类型转换器重用于类似 STL 向量的类?
C++代码(包括pybind11接口)
#include <iostream>
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
// class definition
// ----------------
template<typename T>
class Vector
{
private:
std::vector<T> mData;
public:
Vector(){};
Vector(size_t N) { mData.resize(N); };
auto data () { return mData.data (); };
auto data () const { return mData.data (); };
auto begin() { return mData.begin(); };
auto begin() const { return mData.begin(); };
auto end () { return mData.end (); };
auto end () const { return mData.end (); };
size_t size () const { return mData.size (); };
std::vector<size_t> shape() const { return std::vector<size_t>(1, mData.size()); }
std::vector<size_t> strides() const { return std::vector<size_t>(1, sizeof(T) ); }
template<typename It> static Vector<T> Copy(It first, It last) {
Vector out(last-first);
std::copy(first, last, out.begin());
return out;
}
};
// C++ functions: overload based on type
// -------------------------------------
Vector<int> foo(const Vector<int> &A){ std::cout << "int" << std::endl; return A; }
Vector<double> foo(const Vector<double> &A){ std::cout << "double" << std::endl; return A; }
// pybind11 type caster
// --------------------
namespace pybind11 {
namespace detail {
template<typename T> struct type_caster<Vector<T>>
{
public:
PYBIND11_TYPE_CASTER(Vector<T>, _("Vector<T>"));
bool load(py::handle src, bool convert)
{
if ( !convert && !py::array_t<T>::check_(src) ) return false;
auto buf = py::array_t<T, py::array::c_style | py::array::forcecast>::ensure(src);
if ( !buf ) return false;
auto rank = buf.ndim();
if ( rank != 1 ) return false;
value = Vector<T>::Copy(buf.data(), buf.data()+buf.size());
return true;
}
static py::handle cast(const Vector<T>& src, py::return_value_policy policy, py::handle parent)
{
py::array a(std::move(src.shape()), std::move(src.strides()), src.data());
return a.release();
}
};
}} // namespace pybind11::detail
// Python interface
// ----------------
PYBIND11_MODULE(example,m)
{
m.doc() = "pybind11 example plugin";
m.def("foo", py::overload_cast<const Vector<int > &>(&foo));
m.def("foo", py::overload_cast<const Vector<double> &>(&foo));
}
例子
import numpy as np
import example
print(example.foo((1,2,3)))
print(example.foo((1.5,2.5,3.5)))
print(example.foo(np.array([1,2,3])))
print(example.foo(np.array([1.5,2.5,3.5])))
输出:
int
[1 2 3]
int
[1 2 3]
int
[1 2 3]
double
[1.5 2.5 3.5]
解决方案
一个非常简单的解决方案是专业化pybind11::detail::list_caster
。类型脚轮现在变得像
namespace pybind11 {
namespace detail {
template <typename Type> struct type_caster<Vector<Type>> : list_caster<Vector<Type>, Type> { };
}} // namespace pybind11::detail
请注意,这确实需要Vector
具有以下方法:
clear()
push_back(const Type &value)
reserve(size_t n)
(在测试中似乎是可选的)
完整示例
#include <iostream>
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
// class definition
// ----------------
template<typename T>
class Vector
{
private:
std::vector<T> mData;
public:
Vector(){};
Vector(size_t N) { mData.resize(N); };
auto data () { return mData.data (); };
auto data () const { return mData.data (); };
auto begin() { return mData.begin(); };
auto begin() const { return mData.begin(); };
auto end () { return mData.end (); };
auto end () const { return mData.end (); };
size_t size () const { return mData.size (); };
void push_back(const T &value) { mData.push_back(value); }
void clear() { mData.clear(); }
void reserve(size_t n) { mData.reserve(n); }
std::vector<size_t> shape() const { return std::vector<size_t>(1, mData.size()); }
std::vector<size_t> strides() const { return std::vector<size_t>(1, sizeof(T) ); }
template<typename It> static Vector<T> Copy(It first, It last) {
printf("Vector<T>::Copy %s\n", __PRETTY_FUNCTION__);
Vector out(last-first);
std::copy(first, last, out.begin());
return out;
}
};
// C++ functions: overload based on type
// -------------------------------------
Vector<int> foo(const Vector<int> &A){ std::cout << "int" << std::endl; return A; }
Vector<double> foo(const Vector<double> &A){ std::cout << "double" << std::endl; return A; }
// pybind11 type caster
// --------------------
namespace pybind11 {
namespace detail {
template <typename Type> struct type_caster<Vector<Type>> : list_caster<Vector<Type>, Type> { };
}} // namespace pybind11::detail
// Python interface
// ----------------
PYBIND11_MODULE(example,m)
{
m.doc() = "pybind11 example plugin";
m.def("foo", py::overload_cast<const Vector<double> &>(&foo));
m.def("foo", py::overload_cast<const Vector<int > &>(&foo));
}
推荐阅读
- electron-packager - 如何包含自述文件电子?
- neo4j - neo4j jvm堆内存错误:Neo.TransientError.General.OutOfMemoryError
- ruby-on-rails - Rails ahoy_email gem url 有时会损坏
- python - 如何将字符串列表转换为数组?(不使用 NumPy)
- javascript - 如何遍历对象数组并仅显示子组件中的最新值?
- swift - 如何从 NSObject 子数组数据中过滤 NSObject 数组?
- java - 类中的 Java 方法不能应用于没有参数的给定类型
- python-3.x - 如何允许正则表达式搜索 '\xcf\x83' 这种字符串
- powershell - Jupyterlab 终端不工作,但 windows 中的 cmd 工作正常
- python - Flask - 通过 GET 请求发送数据