用于指针的Boost.Python转换器不起作用

时间:2015-06-02 08:33:22

标签: python c++ pointers boost

TLDR:是否可以通过python转换器传递C ++指针类型?

这里我为它声明了我的MyStruct和指针类型PMYSTRUCT,转换器模板类PointerConverter和那个python模块:

#include <boost/python.hpp>

namespace py = boost::python;


template<class T, class Converter>
void from_python_converter()
{
    py::converter::registry::push_back(&Converter::convertable, &Converter::construct, py::type_id<T>());
}


template<typename POINTER_TYPE>
class PointerConverter
{
public:
    static void* convertable(PyObject *obj_ptr) // Detect: Is Pyhton object converatable?
    {
        static py::object ctypes_c_void_p = py::import("ctypes").attr("c_void_p");
        return PyObject_IsInstance(obj_ptr, ctypes_c_void_p.ptr())? obj_ptr: nullptr;
    }

    static void construct(PyObject *obj_ptr, py::converter::rvalue_from_python_stage1_data *data) // From Python to POINTER_TYPE converter
    {
        auto storage = reinterpret_cast<py::converter::rvalue_from_python_storage<POINTER_TYPE>*>(data)->storage.bytes;
        new(storage) POINTER_TYPE(reinterpret_cast<POINTER_TYPE>(uintptr_t(py::extract<uintptr_t>(py::object(py::handle<>(py::borrowed(obj_ptr))).attr("value")))));
        data->convertible = storage;
    }

struct MyStruct
{
    ...
};

typedef MyStruct* PMYSTRUCT;

void foo(PMYSTRUCT p)
{
    ...
}

BOOST_PYTHON_MODULE(module_name)
{
    from_python_converter<PMYSTRUCT, PointerConverter<PMYSTRUCT>>();

    py::def("foo", &foo);
}

如您所见,foo()计划通过boost.python转换器机制接收PMYSTRUCT作为参数。但是当我试图通过时:

from module_name import foo
import ctypes
foo(ctypes.c_void_p(100))

我得到例外:

Boost.Python.ArgumentError: Python argument types in
    module_name.foo(c_void_p)
did not match C++ signature:
    foo(struct MyStruct * __ptr64)

1 个答案:

答案 0 :(得分:0)

我自己找到了解决方案。虽然不可能为普通指针实现转换器,但我决定制作一个包装器:

Pointer.h:

#pragma once

template<typename POINTER_TYPE>
class Pointer
{
public:
    Pointer(POINTER_TYPE p = nullptr): value(p)
    {
    }

    operator POINTER_TYPE() const
    {
        return value;
    }

    template<typename T>
    T get() const
    {
        return reinterpret_cast<T>(value);
    }

    class Converter
    {
    public:
        Converter()
        {
            py::to_python_converter<Pointer, Converter>();
            py::converter::registry::push_back(&Converter::convertable, &Converter::construct, py::type_id<Pointer>());
            if (ctypes_c_void_p.is_none())
            {
                ctypes_c_void_p = py::import("ctypes").attr("c_void_p");
            }
        }

        static void* convertable(PyObject *obj_ptr)
        {
            return PyObject_IsInstance(obj_ptr, ctypes_c_void_p.ptr())? obj_ptr: nullptr;
        }

        static void construct(PyObject *obj_ptr, py::converter::rvalue_from_python_stage1_data *data)
        {
            // From ctypes.c_void_p
            auto storage = reinterpret_cast<py::converter::rvalue_from_python_storage<Pointer>*>(data)->storage.bytes;
            py::object value_obj = py::object(py::handle<>(py::borrowed(obj_ptr))).attr("value");
            new(storage) Pointer(value_obj.is_none()? 0: reinterpret_cast<POINTER_TYPE>(uintptr_t(py::extract<uintptr_t>(value_obj))));
            data->convertible = storage;
        }

        static PyObject* convert(Pointer const &ptr)
        {
            return py::incref(ctypes_c_void_p(ptr.get<uintptr_t>()).ptr());
        }

    private:
        static py::object ctypes_c_void_p;
    };

    POINTER_TYPE value;
};

template<typename POINTER_TYPE>
py::object Pointer<POINTER_TYPE>::Converter::ctypes_c_void_p;

Main.h:

#include <boost/python.hpp>

namespace py = boost::python;

#include "Pointer.h"

typedef Pointer<PMYSTRUCT> MyStructPtr;

Foo.cpp中:

#include "Main.h"

MyStructPtr foo(MyStructPtr p)
{
    PMYSTRUCT pointer_as_arg = p;

    return new MyStruct;
}

Main.cpp的:

#include "Main.h"

BOOST_PYTHON_MODULE(module_name)
{
    MyStructPtr::Converter();

    py::def("foo", &foo);
}