Double checking: passing out internal reference from member function - python

If I have
//test.hpp
class iA
{
public:
virtual ~iA(){}
virtual void foo() const = 0;
};
class A1 : public iA
{
public:
virtual ~A1(){}
A1(){}
virtual void foo() const;
};
class A2 : public iA
{
public:
virtual ~A2(){}
A2(){}
virtual void foo() const;
};
class B
{
public:
B(int a);
B(const std::string& s);
~B() {if (a_) {delete a_; a_ = 0;}}
const iA& a() const
{
return *a_;
}
void foo() const;
private:
iA* a_;
};
And I wrote the python wrapper below:
struct iAWrap : iA, boost::python::wrapper<iA>
{
void foo() const
{
this->get_override("foo");
}
};
BOOST_PYTHON_MODULE(libtest)
{
using namespace boost::python;
def("greet", greet);
class_<iAWrap, boost::noncopyable>("iA")
.def("foo",pure_virtual(&iA::foo))
;
class_<B>("B",init<int>())
.def(init<std::string>())
.def("foo", &B::foo)
.def("a", &B::a, return_internal_reference<1>())//line I have a question about
;
}
The return_internal_reference<1> binds the lifetime of the A reference returned to the invisible "self" argument of B?

It has always helped me to think of it as though the object being returned (A) extends the lifetime of its owning object (B) to be at least as long as the returned object (A).
The return_internal_reference documentation describes the owner_arg:
The index of the parameter which contains the object to which the reference or pointer is being returned. If used to wrap a member function, parameter 1 is the target object (*this).
In the original code, the owner_arg is explicitly set to 1 indicating that the this object (B) in the member function (&B::a) invocation is the object that contains the returned object (iA).
The lifetime behavior effects is documented in with_custodian_and_ward_postcall, which states:
The ward object will not be destroyed until after the custodian [...]
The return_internal_reference documentation's class synopsis simplifies the actual inheritance chain:
template <std::size_t owner_arg = 1, class BasePolicy_ = default_call_policies>
struct return_internal_reference
: with_custodian_and_ward_postcall<0, owner_arg, BasePolicy_>
{
// ...
};
The return_internal_reference struct:
explicitly provides 0 as the custodian, setting the return object (iA) of postcall() as the custodian in the relationship.
passes the owner_arg (return_internal_reference defaults to 1) as the ward, setting the *this object (B) as the ward in the relationship.
Hence, the B ward object will not be destroyed until after the iA custodian object.
Here is a complete simple example demonstrating this behavior:
#include <iostream>
#include <boost/python.hpp>
class Foo
{
public:
Foo() { std::cout << "Foo()" << std::endl; }
~Foo() { std::cout << "~Foo()" << std::endl; }
};
class Bar
{
public:
Bar() { std::cout << "Bar()" << std::endl; }
~Bar() { std::cout << "~Bar()" << std::endl; }
Foo& foo() { return foo_; }
private:
Foo foo_;
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<Foo>("Foo");
python::class_<Bar>("Bar")
.def("foo", &Bar::foo, python::return_internal_reference<>())
;
}
Interactive Python:
>>> import example
>>> bar = example.Bar()
Foo()
Bar()
>>> foo = bar.foo()
>>> del bar
>>> del foo
~Bar()
~Foo()
Note that the lifetime of the bar ward object was extended to be at least as long as the foo custodian object.

Related

Python C API: How to check if an object is an instance of a type

I want to check if an object is an instance of a certain class. In Python I can do this with instanceof. In C/C++, I found a function named PyObject_IsInstance. But it seems not to work like isinstance.
In detail (also described as sample codes below):
In C++ I defined my custom type My. The type definition is MyType and the object definition is MyObject.
Add MyType to the exported module with name My.
In Python, create a new instance my = My(), and isinstance(my, My) returns True.
While in C++ we use PyObject_IsInstance(my, (PyObject*)&MyType) to check my, and this returns 0, which means my is not an instance of the class defined by MyType.
Full C++ code:
#define PY_SSIZE_T_CLEAN
#include <python3.6/Python.h>
#include <python3.6/structmember.h>
#include <stddef.h>
typedef struct {
PyObject_HEAD
int num;
} MyObject;
static PyTypeObject MyType = []{
PyTypeObject ret = {
PyVarObject_HEAD_INIT(NULL, 0)
};
ret.tp_name = "cpp.My";
ret.tp_doc = NULL;
ret.tp_basicsize = sizeof(MyObject);
ret.tp_itemsize = 0;
ret.tp_flags = Py_TPFLAGS_DEFAULT;
ret.tp_new = PyType_GenericNew;
return ret;
}();
// check if obj is an instance of MyType
static PyObject *Py_fn_checkMy(PyObject *obj) {
if (PyObject_IsInstance(obj, (PyObject *)&MyType)) Py_RETURN_TRUE;
else Py_RETURN_FALSE;
}
static PyMethodDef modmethodsdef[] = {
{ "checkMy", (PyCFunction)Py_fn_checkMy, METH_VARARGS, NULL },
{ NULL }
};
static PyModuleDef moddef = []{
PyModuleDef ret = {
PyModuleDef_HEAD_INIT
};
ret.m_name = "cpp";
ret.m_doc = NULL;
ret.m_size = -1;
return ret;
}();
PyMODINIT_FUNC
PyInit_cpp(void)
{
PyObject *mod;
if (PyType_Ready(&MyType) < 0)
return NULL;
mod = PyModule_Create(&moddef);
if (mod == NULL)
return NULL;
Py_INCREF(&MyType);
PyModule_AddObject(mod, "My", (PyObject *)&MyType);
PyModule_AddFunctions(mod, modmethodsdef);
return mod;
}
Compile this into cpp.so, and test it in Python:
>>> import cpp
>>> isinstance(cpp.My(), cpp.My)
True
>>> cpp.checkMy(cpp.My())
False
METH_VARARGS
This is the typical calling convention, where the methods have the type PyCFunction. The function expects two PyObject* values. The first one is the self object for methods; for module functions, it is the module object. The second parameter (often called args) is a tuple object representing all arguments. This parameter is typically processed using PyArg_ParseTuple() or PyArg_UnpackTuple().
The function signature of Py_fn_checkMy does not match this. It should take two arguments. The first is the module, and this is what you are checking against MyType. The second argument (which you don't actually accept) is a tuple containing the object you passed. You should extract the argument from the tuple and check the type of this.
You'd probably be better using METH_O to specify a single argument instead of extracting arguments from a tuple:
static PyObject *Py_fn_checkMy(PyObject *self, PyObject *obj) {
if (PyObject_IsInstance(obj, (PyObject *)&MyType)) Py_RETURN_TRUE;
else Py_RETURN_FALSE;
}
static PyMethodDef modmethodsdef[] = {
{ "checkMy", (PyCFunction)Py_fn_checkMy, METH_O, NULL },
{ NULL }
};

Pybind11: Transfer Class Ownership to C++ on Construct

I'm having an issue where a python class, which is derived from a c++ base class using pybind11, is being immediately destructed (garbage collected). I would like C++ to take ownership of the dynamically allocated object, but I can't seem to make that happen. I've tried keep_alive, passing shared_ptr<> as py::class_ template argument, and py::return_value_policy... nothing is working. I suspect this is just user error.
This is a simplification of the real issue I'm having with a much larger code base that is architected similarly. Changing the architecture is not an option, so making this example work is critical for me.
I have two c++ classes that I have created python interfaces for using pybind11. Class A and B both have virtual methods, so they have corresponding trampoline classes to support inheritance. The user calls the B::Run() function which results in a dynamically allocated (via new) A object to be created. When I create specializations of these two classes in python, as shown below.... Segmentation fault because the B::aBase is destroyed immediately after B::Run being called.
Any Ideas how to fix this? Thanks in advance!
class A
{
public:
A(){};
virtual ~A()
{
std::cout << "In A::~A()\n";
};
virtual char* SayHello()
{
char* x = "\n\nHello from Class A\n\n";
return x;
}
};
class ATramploline : public A
{
public:
using A::A;
char* SayHello() override
{
PYBIND11_OVERLOAD( char*,A,SayHello,);
}
};
class B
{
public:
B()
{
std::cout << "In Class B Constructor\n";
}
void Run()
{
aBase = AllocateAnAClass();
std::cout << aBase->SayHello();
}
virtual ~B()
{
fprintf(stderr,"About to delete aBase");
delete aBase;
}
A* aBase;
virtual A* AllocateAnAClass()
{
return new A;
}
};
class BTramploline : public B
{
public:
using B::B;
A* AllocateAnAClass() override
{
PYBIND11_OVERLOAD( A*,B,AllocateAnAClass,);
}
};
PYBIND11_MODULE(TestModule,m)
{
py::class_<A,ATramploline>(m,"A")
.def(py::init<>(),py::return_value_policy::reference_internal)
.def("SayHello",&A::SayHello);
py::class_<B,BTramploline>(m,"B")
.def(py::init<>())
.def("Run",&B::Run)
.def("AllocateAnAClass",&B::AllocateAnAClass,py::return_value_policy::reference_internal);
}
#!/usr/bin/python3
from TestModule import A,B
class MyA(A):
def __init__(self):
super().__init__()
print("Done with MyA Constructor")
def SayHello(self):
return '\n\nHello from Class MyA\n\n'
class MyB(B):
def __init__(self):
super().__init__()
print("Done With MyB Constructor")
def AllocateAnAClass(self):
print("In MyB::AllocateAnAClass!!!")
return MyA()
#x = B()
#x.Run()
y = MyB()
y.Run()
print("done with test script\n")
The correct (I think) way to use std::shared_ptr<A> as the A holder is to add it to class_<A...> arguments.
You also want to replace every instance of A* with std::shared_ptr<A>, and new with std::make_shared. I think non-default return policies are not needed in this case, so I have removed them; YMMV.
Working module below (with minor errors corrected).
#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>
namespace py = pybind11;
class A
{
public:
A(){};
A(const A&) { std::cout << "Copying A\n"; }
virtual ~A()
{
std::cout << "In A::~A()\n";
};
virtual const char* SayHello()
{
const char* x = "\n\nHello from Class A\n\n";
return x;
}
};
class ATrampoline : public A
{
public:
using A::A;
const char* SayHello() override
{
PYBIND11_OVERLOAD( const char*,A,SayHello,);
}
};
class B
{
public:
B()
{
std::cout << "In Class B Constructor\n";
}
B(const B&) { std::cout << "Copying B\n"; }
void Run()
{
aBase = AllocateAnAClass();
std::cout << aBase->SayHello();
}
virtual ~B()
{
}
std::shared_ptr<A> aBase;
virtual std::shared_ptr<A> AllocateAnAClass()
{
return std::make_shared<A>();
}
};
class BTrampoline : public B
{
public:
using B::B;
std::shared_ptr<A> AllocateAnAClass() override
{
PYBIND11_OVERLOAD(std::shared_ptr<A>,B,AllocateAnAClass,);
}
};
PYBIND11_MODULE(TestModule,m)
{
py::class_<A,std::shared_ptr<A>, ATrampoline>(m,"A")
.def(py::init<>())
.def("SayHello",&A::SayHello);
py::class_<B, BTrampoline>(m,"B")
.def(py::init<>())
.def("Run",&B::Run)
.def("AllocateAnAClass",&B::AllocateAnAClass);
}
py::nodelete was the solution. While n.m's answer DOES work, it would require going back and chaning all of the pointer in an existing libary to smart pointers, which isn't a viable option for me. Using py::nodelete allows me to do everything on the pybind11 side.
py::class_<A,ATramploline,std::unique_ptr<A,py::nodelete> >(m,"A")
.def(py::init<>())
.def("SayHello",&A::SayHello);

How do I use static keyword inside class in C++ to emulate the behavior of #classmethod in Python?

I read somewhere that #classmethod in Python is similar to static member function in C++. But what is the equivalent cls parameter in C++ and how do I pass it?
Below is a Python code that uses inheritance and #classmethod,
class Parent():
def __init__(self, name):
self._name = name
#classmethod
def produce(cls, name):
return cls(name)
def say_my_name(self):
print("I am parent", self._name)
class Child(Parent):
def say_my_name(self):
print("I am child", self._name)
p1 = Parent("One")
p1.say_my_name()
p2 = Parent.produce("Two")
p2.say_my_name()
p1 = Child("One")
p1.say_my_name()
p2 = Child.produce("Two")
p2.say_my_name()
And now I am stuck in my incomplete C++ code as follows
class Parent
{
protected:
std::string name;
public:
Parent(const std::string& name): name{name} {};
// Can I not use static in the next statement?
// Parent is already hard-coded, what's the cls in C++?
static Parent produce(const std::string& name) const
{
return Parent {name};
}
void say_my_name() const
{
std::cout << "I am parent " << name << "\n";
}
};
How can I emulate my Python code using C++?
return Parent {name}; is correct. However, a static member function cannot be const, since there is no object on which it is called.
There is no built-in way to specify "the type of the current class" in C++. You just have to write Parent again. Unfortunately, that means that if you change the name of the class, you also have to change all relevant occurrences in the class's implementation.
In order to emulate the use case you demonstrate in the Pynthon code some repetition is required:
#include <iostream>
#include <string>
class Parent
{
protected:
std::string name;
public:
Parent() = default;
Parent(const std::string& name) : name{name} {};
using cls = Parent; // this isn't normally done, normally Parent is just repeated
// Can I not use static in the next statement?
// Parent is already hard-coded, what's the cls in C++?
//static Parent produce(const std::string& name) const
cls produce(const std::string& name) const // static would mean the function does not belong to any object, so it must be removed if we want to call it on a temporary const object
{
return cls{name};
}
void say_my_name() const
{
std::cout << "I am parent " << name << "\n";
}
};
class Child : public Parent {
public:
using cls = Child; // this isn't normally done, normally Child is just repeated
Child() = default;
Child(const std::string& name) : Parent{name} {};
void say_my_name() const
{
std::cout << "I am child " << name << "\n";
}
cls produce(const std::string& name) const // have to repeat produce() here so that it hides Parent::produce().
{
return cls{name};
}
};
int main()
{
auto p1 = Parent("One");
p1.say_my_name();
auto p2 = Parent().produce("Two");
p2.say_my_name();
auto c1 = Child("One");
c1.say_my_name();
auto c2 = Child().produce("Two");
c2.say_my_name();
}
This produces:
I am parent One
I am parent Two
I am child One
I am child Two
Here is a slightly modified version that uses static:
#include <iostream>
#include <string>
class Parent
{
protected:
std::string name;
public:
Parent(const std::string& name) : name{name} {};
using cls = Parent; // this isn't normally done, normally Parent is just repeated
// Can I not use static in the next statement?
// Parent is already hard-coded, what's the cls in C++?
//static Parent produce(const std::string& name) const
static cls produce(const std::string& name) // here const would not make sense, this function will not be called on a particular instance of Parent
{
return cls{name};
}
void say_my_name() const
{
std::cout << "I am parent " << name << "\n";
}
};
class Child : public Parent {
public:
using cls = Child; // this isn't normally done, normally Child is just repeated
Child(const std::string& name) : Parent{name} {};
void say_my_name() const
{
std::cout << "I am child " << name << "\n";
}
static cls produce(const std::string& name) // have to repeat produce() here so that it hides Parent::produce().
{
return cls{name};
}
};
int main()
{
auto p1 = Parent("One");
p1.say_my_name();
auto p2 = Parent::produce("Two"); // No instance of Parent is used to create p2
p2.say_my_name();
auto c1 = Child("One");
c1.say_my_name();
auto c2 = Child::produce("Two");
c2.say_my_name();
}
If however, we were to start from a clean slate, and wanted the result above. We could write:
#include <iostream>
#include <string>
class Parent
{
protected:
std::string name;
public:
Parent(const std::string& name) : name{name} {};
void say_my_name() const
{
std::cout << "I am parent " << name << "\n";
}
};
class Child : public Parent {
public:
Child(const std::string& name) : Parent{name} {};
void say_my_name() const
{
std::cout << "I am child " << name << "\n";
}
};
template <typename T>
T produce(const std::string& name)
{
return{name};
}
int main()
{
Parent p1{"One"};
p1.say_my_name();
auto p2 = produce<Parent>("Two");
p2.say_my_name();
Child c1{"One"};
c1.say_my_name();
auto c2 = produce<Child>("Two");
c2.say_my_name();
}

boost::python protected destructor issue

namespace test_py
{
class Event
{
public:
enum Type { BEGIN = 0, RESULT, END };
Type get_type( ) const { return m_type; }
protected:
Event( ) { }
~Event( ) { }
Type m_type;
};
class EventBegin : public Event
{
public:
EventBegin( ) { m_type = Event::BEGIN; }
~EventBegin( ) {}
};
class EventResult : public Event
{
public:
EventResult( int result ) { m_type = Event::RESULT; m_result = result; }
~EventResult( ) {}
int get_result( ) { return m_result; }
protected:
int m_result;
};
class EventEnd : public Event
{
public:
EventEnd( ) { m_type = Event::END; }
~EventEnd( ) {}
};
class EventListener
{
public:
virtual void on_event( const Event& event ) = 0;
};
struct EventListenerWrap: EventListener, py::wrapper< EventListener >
{
void
on_event( const Event& event )
{
this->get_override( "on_event" )( event );
}
};
BOOST_PYTHON_MODULE( test_py )
{
{
py::scope outer = py::class_< Event, boost::noncopyable >( "Event", py::no_init )
.add_property( "event_type", &Event::get_type );
py::enum_< Event::Type >( "EventType" )
.value( "BEGIN", Event::BEGIN )
.value( "RESULT", Event::RESULT )
.value( "END", Event::END )
.export_values( );
}
{
py::class_< EventBegin, py::bases< Event > >( "EventBegin" );
}
{
py::class_< EventResult, py::bases< Event > >( "EventResult", py::no_init )
.def( py::init< int >( ( py::arg( "result" ) ) ) )
.add_property( "result", &EventResult::get_result );
}
{
py::class_< EventEnd, py::bases< Event > >( "EventEnd" );
}
{
py::class_< EventListenerWrap, boost::noncopyable >( "EventListener", py::no_init )
.def( "on_event", py::pure_virtual( &EventListener::on_event ) );
}
}
}
I have a protected constructor and destructor in Event base class and cannot change that.
In Python 2.7 I need to derive from EventListener class and send pointer back to C++ code.
During compilation I got error like that:
/boost/python/detail/destroy.hpp: In instantiation of ‘static void boost::python::detail::value_destroyer<false>::execute(const volatile T*) [with T = test_py::Event]’:
/boost/python/detail/destroy.hpp:95:36: required from ‘void boost::python::detail::destroy_referent_impl(void*, T& (*)()) [with T = const test_py::Event]’
/boost/python/detail/destroy.hpp:101:39: required from ‘void boost::python::detail::destroy_referent(void*, T (*)()) [with T = const test_py::Event&]’
/boost/python/converter/rvalue_from_python_data.hpp:135:71: required from ‘boost::python::converter::rvalue_from_python_data<T>::~rvalue_from_python_data() [with T = const test_py::Event&]’
/boost/python/converter/arg_from_python.hpp:107:8: required from ‘PyObject* boost::python::detail::caller_arity<2u>::impl<F, Policies, Sig>::operator()(PyObject*, PyObject*) [with F = void (test_py::EventListener::*)(const test_py::Event&); Policies = boost::python::default_call_policies; Sig = boost::mpl::vector3<void, test_py::EventListener&, const test_py::Event&>; PyObject = _object]’
/boost/python/object/py_function.hpp:38:33: required from ‘PyObject* boost::python::objects::caller_py_function_impl<Caller>::operator()(PyObject*, PyObject*) [with Caller = boost::python::detail::caller<void (test_py::EventListener::*)(const test_py::Event&), boost::python::default_call_policies, boost::mpl::vector3<void, test_py::EventListener&, const test_py::Event&> >; PyObject = _object]’
EventListener.cpp:193:1: required from here
EventListener.cpp:18:5: error: ‘test_py::Event::~Event()’ is protected
~Event( ) { }
^
In file included from /boost/python/converter/rvalue_from_python_data.hpp:10:0,
from /boost/python/converter/registry.hpp:9,
from /boost/python/converter/registered.hpp:8,
from /boost/python/object/make_instance.hpp:10,
from /boost/python/object/make_ptr_instance.hpp:8,
from /boost/python/to_python_indirect.hpp:11,
from /boost/python/converter/arg_to_python.hpp:10,
from /boost/python/call.hpp:15,
from /boost/python/object_core.hpp:14,
from /boost/python/object/class.hpp:9,
from /boost/python/class.hpp:13,
from ../../defs.hpp:6,
from ../defs.hpp:3,
from defs.hpp:3,
from EventListener.cpp:1:
/boost/python/detail/destroy.hpp:33:9: error: within this context
p->~T();
^
py::scope outer = py::class_< Event, boost::noncopyable >( "Event", py::no_init )
.add_property( "event_type", &Event::get_type );
First glance tells me you have a problem here. py::class_<Event, ...> only knows about binding to the Event, which has the protected destructor.
You're going to have to wrap Event in a class that exposes the destructor publically.
If that's not possible (because you cant change the definition of EventBegin, EventEnd etc for example) then you're going to have to write a polymorphic container that holds on to the derived classes through its own internal interface, internally treating the events as non-polymorphic objects.
This is not as difficult as it sounds:
#include <memory>
namespace test_py
{
class Event
{
public:
enum Type { BEGIN = 0, RESULT, END };
Type get_type( ) const { return m_type; }
protected:
Event( ) { }
~Event( ) { }
Type m_type;
};
class EventBegin : public Event
{
public:
EventBegin( ) { m_type = Event::BEGIN; }
~EventBegin( ) {}
};
class EventResult : public Event
{
public:
EventResult( int result ) { m_type = Event::RESULT; m_result = result; }
~EventResult( ) {}
int get_result( ) { return m_result; }
protected:
int m_result;
};
class EventEnd : public Event
{
public:
EventEnd( ) { m_type = Event::END; }
~EventEnd( ) {}
};
class EventProxy
{
// define an interface for turning a non-polymorphic event
// into a polymorphic one
struct concept
{
virtual const Event* as_event() const = 0;
virtual ~concept() = default;
};
// define a model to support the polymorphic interface for a
// non-polymorphic concrete object
template<class T> struct model : concept
{
template<class...Args> model(Args&&... args)
: _event(std::forward<Args>(args)...)
{}
const Event* as_event() const override {
return &_event;
}
T _event;
};
// construct the model that contains any Event
template<class T>
EventProxy(std::shared_ptr<T> ptr)
: _impl(std::move(ptr))
{}
public:
// T should be derived from Event...
template<class T, class...Args>
static EventProxy create(Args&&... args)
{
return EventProxy(std::make_shared<model<T>>(std::forward<Args>(args)...));
}
// simply return the address of the internal non-polymorphic event
const Event* as_event() const {
return _impl->as_event();
}
// return a shared pointer that points to the internal Event BUT
// defers lifetime ownership to our internal shared_ptr to
// our model. This means we never invoke the polymorphic
// destructor of Event through the protected interface.
std::shared_ptr<const Event> as_shared_event() const {
return std::shared_ptr<const Event>(_impl, _impl->as_event());
}
private:
// lifetime of the proxy is owned by this shared_ptr.
std::shared_ptr<concept> _impl;
};
}
// a quick test.
auto main() -> int
{
auto ep = test_py::EventProxy::create<test_py::EventBegin>();
const test_py::Event* p = ep.as_event();
std::shared_ptr<const test_py::Event> sp = ep.as_shared_event();
}
When exposing a function, Boost.Python will generate converters for each of the arguments. For arguments with types T and T&, the resulting Python converter will hold a copy of the object, and hence needs access to the copy-constructor and destructor. The rationale for this behavior is to prevent accidentally exposing dangling references. The same holds true when passing C++ arguments to Python.
This behavior presents a problem when:
exposing EventListener::on_event(const Event&), as Boost.Python is attempting to create an object that will hold a copy of the Event. To resolve this, consider exposing an auxiliary function that accepts a Event*, and then delegates to the original function.
passing an Event object to the Python in EventListenerWrap::on_event. To resolve this, consider wrapping the argument in boost::ref() or boost::python::ptr().
Be aware that by not creating copies, it creates the chance for dangling references. If the actual Event object is owned by Python, then its lifetime needs to be at least as long as any reference to it in C++. Likewise. If the actual Event object is owned by C++, then its lifetime needs to be at least as long as any reference to it in Python.
struct EventListenerWrap
: EventListener,
boost::python::wrapper<EventListener>
{
void on_event(const Event& event)
{
this->get_override("on_event")(boost::ref(event));
}
};
/// #brief Auxiliary function that will delegate to EventListener::on_event and
/// avoid by-value conversions at the language boundary. This prevents
/// prevents Boost.Python from creating instance holders that would hold
/// the value as an rvalue.
void event_listener_on_event_aux(EventListener& listener, Event* event)
{
return listener.on_event(*event);
}
BOOST_PYTHON_MODULE(...)
{
namespace python = boost::python;
python::class_<EventListenerWrap, boost::noncopyable>("EventListener")
.def("on_event", python::pure_virtual(&event_listener_on_event_aux))
;
}
An interesting detail is that boost::python::pure_virtual() will duplicate the signature of the function it wraps, but it will never actual invoke the wrapped function. Hence, the wrapped function could have a no-op/empty implementation, but providing an implementation is a good idea incase the pure_virtual designator is removed or if the auxiliary function is invoked directly.
Also, note that to allow a Python class to derive from a Boost.Python class, the Boost.Python must expose an __init__() method. Providing no methods, such as by using boost::python::no_init(), will result in a runtime error.
Here is a minimal complete example based on the original code that demonstrates exposing a class with protected constructor and destructor, two derived classes, and virtual dispatch of the derived classes through Boost.Python:
#include <iostream>
#include <string>
#include <boost/python.hpp>
// Legacy API.
class event
{
public:
std::string name;
protected:
event(std::string name) : name(name) {}
~event() = default;
};
struct event_a: event { event_a(): event("event_a") {} };
struct event_b: event { event_b(): event("event_b") {} };
class event_listener
{
public:
virtual void on_event(const event& event) = 0;
};
// Boost.Python helper types and functions.
struct event_listener_wrap
: event_listener,
boost::python::wrapper<event_listener>
{
void on_event(const event& event)
{
std::cout << "event_listener_wrap::on_event()" << std::endl;
this->get_override("on_event")(boost::ref(event));
}
};
/// #brief Auxiliary function that will delegate to EventListener::on_event and
/// avoid by-value conversions at the language boundary. This prevents
/// prevents Boost.Python from creating instance holders that would hold
/// the value as an rvalue.
void event_listener_on_event_wrap(event_listener& listener, event* event)
{
return listener.on_event(*event);
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose event and suppress default by-value converters and initailizers.
// This will prevent Boost.Python from trying to access constructors and
// destructors.
python::class_<event, boost::noncopyable>("Event", python::no_init)
.def_readonly("name", &event::name)
;
// Expose event_a and event_b as derived from event.
python::class_<event_a, python::bases<event>>("EventA");
python::class_<event_b, python::bases<event>>("EventB");
// Expose event_listener_wrap.
python::class_<event_listener_wrap, boost::noncopyable>("EventListener")
.def("on_event", python::pure_virtual(&event_listener_on_event_wrap))
;
// Expose a function that will perform virtual resolution.
python::def("do_on_event", &event_listener_on_event_wrap);
}
Interactive usage:
>>> import example
>>> class Listener(example.EventListener):
... def on_event(self, event):
... assert(isinstance(event, example.Event))
... print "Listener::on_event: ", event, event.name
...
>>> listener = Listener()
>>> listener.on_event(example.EventA())
Listener::on_event: <example.EventA object at 0x7f3bc1176368> event_a
>>> example.do_on_event(listener, example.EventB())
event_listener_wrap::on_event()
Listener::on_event: <example.Event object at 0x7f3bc1246fa0> event_b
When Python is directly aware of method, it will invoke it without passing through Boost.Python. Notice how listener.on_event() did not get dispatched through C++, and event object maintains its example.EventA type. On the other hand, when dispatching is forced into C++, upcasting will not occur. When Listener.on_event() is invoked through example.do_on_event(), the event object's type is example.Event and not example.EventB.

Python cannot create instances

I am trying to create a simple Python extension using PyCXX. And I'm compiling against my Python 2.5 installation.
My goal is to be able to do the following in Python:
import Cats
kitty = Cats.Kitty()
if type(kitty) == Cats.Kitty:
kitty.Speak()
But every time I try, this is the error that I get:
TypeError: cannot create 'Kitty' instances
It does see Cats.Kitty as a type object, but I can't create instances of the Kitty class, any ideas?
Here is my current source:
#include "CXX/Objects.hxx"
#include "CXX/Extensions.hxx"
#include <iostream>
using namespace Py;
using namespace std;
class Kitty : public Py::PythonExtension<Kitty>
{
public:
Kitty()
{
}
virtual ~Kitty()
{
}
static void init_type(void)
{
behaviors().name("Kitty");
behaviors().supportGetattr();
add_varargs_method("Speak", &Kitty::Speak);
}
virtual Py::Object getattr( const char *name )
{
return getattr_methods( name );
}
Py::Object Speak( const Py::Tuple &args )
{
cout << "Meow!" << endl;
return Py::None();
}
};
class Cats : public ExtensionModule<Cats>
{
public:
Cats()
: ExtensionModule<Cats>("Cats")
{
Kitty::init_type();
initialize();
Dict d(moduleDictionary());
d["Kitty"] = Type((PyObject*)Kitty::type_object());
}
virtual ~Cats()
{
}
Py::Object factory_Kitty( const Py::Tuple &rargs )
{
return Py::asObject( new Kitty );
}
};
void init_Cats()
{
static Cats* cats = new Cats;
}
int main(int argc, char* argv[])
{
Py_Initialize();
init_Cats();
return Py_Main(argc, argv);
return 0;
}
I do'nt see it in the code, but sort of thing normally means it can't create an instance, which means it can't find a ctor. Are you sure you've got a ctor that exactly matches the expected signature?

Categories