简体   繁体   中英

Boost.Python converter for pointers doesn't work

TLDR: Is it possible to pass C++ pointer types through python converters?

Here I declared my MyStruct and pointer type for it PMYSTRUCT, converter template class PointerConverter and that python module:

#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);
}

As you see foo() was planned to receive PMYSTRUCT as an argument through the boost.python converter mechanism. But when I try to pass:

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

I get the exception:

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

I found solution by myself. Whereas it's impossible to implement a converter for a plain pointer, I decided to make a wrapper:

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM