[C++-sig] Converting base type to derived type when returning to Python

Albert Strasheim fullung at gmail.com
Tue Jan 16 00:16:47 CET 2007


Hello Roman/all

I think the problem I'm running into is that the library I'm trying to 
wrap returns something that is derived from something that is derived 
from Message. The interface I'm trying to wrap exists exactly to hide 
these details. For the ActiveMQ C++ library I have all the sources 
available, but a commercial library built on top of the CMS interface 
might not expose its internal classes.

Here's a simplified code snippet to show the problem I'm having.

First, a setup.py to build on Linux:

from distutils.core import setup
from distutils.extension import Extension
files = ['messaging.cpp']
setup(name='messaging',
      ext_modules=[
        Extension('messaging', ['messaging.cpp'],
                  library_dirs=[],
                  libraries=['boost_python'],
                  include_dirs=[],
                  depends=[]),
        ])

The source:

#include <boost/python.hpp>

using namespace boost::python;

#define TEXTMESSAGE_IS_INTERFACE 0

struct Message{virtual ~Message(){}};
#if TEXTMESSAGE_IS_INTERFACE
struct TextMessage : public Message{
virtual ~TextMessage(){}
virtual std::string getText() const = 0;};
struct TextMessageImpl : public TextMessage{
virtual ~TextMessageImpl(){}
virtual std::string getText() const{return "hello";}};
#else
struct TextMessage : public Message{
virtual ~TextMessage(){}
virtual std::string getText() const{return "hello";}};
#endif

struct MessageConsumer{
Message* receive(){
#if TEXTMESSAGE_IS_INTERFACE
return new TextMessageImpl();
#else
return new TextMessage();
#endif
}};

BOOST_PYTHON_MODULE(messaging)
{
class_<Message, boost::noncopyable>("Message", no_init);
class_<TextMessage, bases<Message>, boost::noncopyable>("TextMessage", no_init)
.add_property("text", &TextMessage::getText);
class_<MessageConsumer, boost::noncopyable>("MessageConsumer")
.def("receive", &MessageConsumer::receive, 
return_value_policy<manage_new_object>());
}

And the test:

#!/usr/bin/env python

import os.path
import sys
from distutils.util import get_platform
plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3])
build_platlib = os.path.join('build', 'lib' + plat_specifier)
sys.path.insert(0, build_platlib)
from messaging import *
consumer = MessageConsumer()
msg = consumer.receive()
print msg

Run it like this:

rm -rf build ; python setup.py build ; ./test.py

With TEXTMESSAGE_IS_INTERFACE set to 0, I get:

<messaging.TextMessage object at 0xb7f04a74>

but with TEXTMESSAGE_IS_INTERFACE set to 1, I get:

<messaging.Message object at 0xb7ee1a74>

So the question is: is there a way to convince Boost to convert my 
Message* to a TextMessage* (which in turn points to a TextMessageImpl) 
if I can't wrap TextMessageImpl?

Thanks!

Regards,

Albert



More information about the Cplusplus-sig mailing list