pybind11 icon indicating copy to clipboard operation
pybind11 copied to clipboard

py::cast variant using type_info instead of template argument for non-virtual types.

Open 2bam opened this issue 8 years ago • 1 comments

Issue description

It would be a nice feature to have a non-template casting function using type_info for non-virtual types. I gather pybind11 for static-types binds the type_info to the first export of the type by ptr (if it was a Base it gets locked in Base, if it was a Derived stays a Derived, etc)

I made a workaround by accessing the detail part of pybind, so I guess it could change eventually.

Use case

In C++ I have dynamic-bound factories wrappers like Base *Create( int id ) T *Create<T>() { return static_cast<T*>(Create( id_map[typeid(T)] )); }.

To avoid having to make one py-binding for each type (non-scalable), I export a py::object Create(int id) that changes the return type according to a type_info.

Setup

pybind11 2.2.1

Example code

DIRECT TYPE

py::object castComponentByTypeInfo(Base *src, const std::type_info &type) {
	using T1 = pybind11::detail::type_caster_generic;

	auto &st = T1::src_and_type(src, type, nullptr);
	assert(st.second != nullptr);

	auto handle = T1::cast(
		st.first, py::return_value_policy::reference, {}, st.second,
		nullptr, nullptr
	);
	return py::reinterpret_borrow<py::object>(handle);
}

HELD TYPE

py::object castComponentByTypeInfo(holder<Base> src, const std::type_info &type) {
	using T1 = pybind11::detail::type_caster_generic;

	auto &st = T1::src_and_type(src.get(), type, nullptr);
	assert(st.second != nullptr);

	auto handle = T1::cast(
		st.first, py::return_value_policy::take_ownership, {}, st.second,
		nullptr, nullptr, &src);

	return py::reinterpret_steal<py::object>(handle);
}

2bam avatar Oct 26 '17 19:10 2bam


#define HAVE_ROUND
#include <pybind11/pybind11.h>
#include <iostream>
#include <stdexcept>

//////////////
// TYPES
//////////////
class C {
public:
    int something;
};

template<typename T>
struct LousyPool {
    static T poolbuf[1];    
    static int poolver[1];
};

C LousyPool<C>::poolbuf[1] {0};
int LousyPool<C>::poolver[1] {0};

//////////////
// HOLDER TYPE
//////////////

template<typename T>
class Holder {
public:
    int index;
    int version;

    Holder() : index(-1), version(-1) {}
    Holder(int index, int version) : index(index), version(version) {}
    Holder(const Holder& o) : index(o.index), version(o.version) {}

    // We don't want this constructor to be called from Python
    explicit Holder(const T* junk) { throw std::exception("Never called"); }

    T *get() const {
        std::cerr << "GET " << this << " " << index << " " << version << " " << LousyPool<T>::poolver[index] << std::endl;
        // Check if the version matches
        if (version != LousyPool<T>::poolver[index]) {
            throw std::exception(("Version mismatch, dangling ref version " + std::to_string(version) + " current one " + std::to_string(LousyPool<T>::poolver[index])).c_str());
        }
        return LousyPool<T>::poolbuf + index;
    }
};

// Declare the custom holder type for pybind11
PYBIND11_DECLARE_HOLDER_TYPE(T, Holder<T>);

//////////////
// FUNCS
//////////////

Holder<C> c_acquire() {
    auto p = Holder<C>(0, LousyPool<C>::poolver[0]);
    p.get()->something = 123;
    return p;
}

int c_touch(Holder<C> p) {
    return p.get()->something;
}

void c_release(Holder<C> p) {
    // Increment the version to simulate object state change
    LousyPool<C>::poolver[p.index]++;
}

//////////////
// BINDINGS
//////////////

PYBIND11_MODULE(pybind11_bugs_lib, m) {
    pybind11::class_<C, Holder<C>>(m, "C")
        .def_readwrite("something", &C::something)
        ;    
    m.def("c_acquire", &c_acquire);
    m.def("c_release", &c_release);
    m.def("c_touch", &c_touch);
}

ljluestc avatar Dec 24 '24 19:12 ljluestc