Boost logo

Boost :

From: David Rostowsky (drostowsky_at_[hidden])
Date: 2004-04-01 11:02:21


Thanks for the feedback! Yes I am glad someone is posting me message! :)
Heres my full.cpp file that will hopefully shed some light on my Boost
bumbling. Below that is my python script too.

I was just typing the code snippets from memory and I noticed I misled on my
virtual function return value. In fact, its a void return value.

It does throw a runtime exception. Ive commented the line of code in main()
where it throws the exception.

Thanks for all the help! If I can get these passing C++ pointers to Python
issues hammered out, Ill forever proclaim Boost's greatness, and Ill never
look at Lua (or Perl) again ;)

-Dave

#include <windows.h>
#include <iostream>
#include <string>
#include <cstdio>
#include <conio.h>

#undef _DEBUG // just forcing the non-debug library by doing this
#include <Python.h>
#define _DEBUG // just forcing the non-debug library by doing this

#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
#include <boost/python/str.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/module.hpp>
#include <boost/python/call_method.hpp>
#include <boost/python/handle.hpp>

#include <boost/python.hpp>
using namespace boost::python;

// Error codes
#define Success 0
#define PY_InitError -100
#define PY_ConvertError -101
#define PY_CallError -102

// Global module pointer
boost::python::handle<> my_module;

////////////////////////////////////////////////////////////////////////////
//////////
////////////////////////////////////////////////////////////////////////////
//////////
class Base1
{
public:
 int z;
 Base1() { z= 1; };

    /*
    ** I dont want Python to even care about this function! My other C++
code classes may (or may not)
    ** override this function. No Python script should ever override this.
The default behavior is
    ** to do nothing. I figured if I dont expose it to the
BOOST_PYTHON_MODULE, why would it need
    ** to care about this function? Again, if I comment this out, everything
works perfectly. Otherwise,
    ** I get a runtime exception about non_rtti_object.
    */
 virtual void OnKeyUp(unsigned short virtual_key) { };
};

////////////////////////////////////////////////////////////////////////////
//////////
////////////////////////////////////////////////////////////////////////////
//////////
class TestC : public Base1
{
protected:
 int x;

public:

 TestC() { x = 0; };
 void Set(int val) { x = val; };
 int Get(void) { return x; };
    void printPtr() { printf("C++ : Ptr=0x%p", this); }
};

////////////////////////////////////////////////////////////////////////////
//////////
// Python wrapper for TestC
////////////////////////////////////////////////////////////////////////////
//////////
BOOST_PYTHON_MODULE(grumsh)
{
    using namespace boost::python;

 object testClass = class_<TestC, boost::shared_ptr<TestC> >(
       "TestC", init<>())
       .def("Get", &TestC::Get)
       .def("Set", &TestC::Set)
       .def("printPtr", &TestC::printPtr)
    ;

} /* grumsh */

////////////////////////////////////////////////////////////////////////////
//////////
// Function prototypes
////////////////////////////////////////////////////////////////////////////
//////////
int initPython(std::string scriptName);
int callPython(std::string func, std::string msg, boost::python::object&
obj);

////////////////////////////////////////////////////////////////////////////
//////////
// Convenience functions
////////////////////////////////////////////////////////////////////////////
//////////
boost::python::handle<> import_module(std::string module_name)
{
 using namespace boost::python;

    PyObject *module_ptr =
PyImport_ImportModule(const_cast<char*>(module_name.c_str()));
    if (module_ptr == NULL)
    {
  OutputDebugString("import_module, NULL\n");
  exit(1);
 }

 return handle<>(module_ptr);

} /* import_module */

////////////////////////////////////////////////////////////////////////////
//////////
////////////////////////////////////////////////////////////////////////////
//////////
boost::python::object get_function(const char * func_name)
{
 // Borrowed reference
 PyObject * dict = PyModule_GetDict(my_module.get());

    boost::python::str funcName(func_name);

    return
boost::python::object(boost::python::handle<>(PyDict_GetItem(dict,
funcName.ptr())));

} /* get_function */

////////////////////////////////////////////////////////////////////////////
//////////
// Call a function in module (ptrPyModule) with two arguments
// func: Name of python function
// msg: Message string
// obj: Boost.Python object (an instance of a wrapped class)
////////////////////////////////////////////////////////////////////////////
//////////
int callPython(const std::string funcStr,
      std::string msg,
      boost::python::object& obj)
{
int rval;

    boost::python::object func =
get_function(const_cast<char*>(funcStr.c_str()));

    rval = boost::python::extract<int>(func(msg, obj));

    return rval;

} /* callPython */

////////////////////////////////////////////////////////////////////////////
//////////
// Initialize Python interpreter
// Initialize Extension module that expose C++ classes/objects to Python
script
// Set up sys.path to include script path (Where the python scripts can be
found)
////////////////////////////////////////////////////////////////////////////
//////////
int initPython(std::string scriptName)
{
 if(scriptName.length() == 0)
        return PY_InitError;

    // Register the extension module
    PyImport_AppendInittab("grumsh", initgrumsh);

    // Initialize the Python interpreter
    Py_Initialize();

 PySys_SetPath(strcat(Py_GetPath(),
";.\\scripts\\;\\code\\cpp\\python\\scripts\\"));
 char * oldpath = Py_GetPath();

    // Import the module, storing the pointer in a handle<>
    my_module = import_module(const_cast<char*>(scriptName.c_str()));

    return Success;
}

int main()
{
 int rval;

 /* Create a new Test class instance. */
 TestC * test = new TestC();

 /*
 ** Try any way to wrap it such that I can pass a pointer to my Python
script so it can call
 ** my exposed member functions.
 */
 boost::shared_ptr<TestC> test1ptr(test);

 std::cout << "C++ : Initializing Python and loading script" << std::endl;

    int retv = initPython("test_script");
    if( retv == Success )
    {
        test1ptr->printPtr(); std::cout << std::endl;

        std::cout << "C++ : test1ptr->Get() in C++ before Python call: "
<< test1ptr->Get() << std::endl;

        /*
        ** This is where I get a RUNTIME exception (non_rtti_object). If I
comment out the virtual
        ** member function out of the class Base1, this works perfectly!
        */
  object arg2(test1ptr);

        /* This works great too as long as I dont have the virtual member
function. */
        int retv = callPython("myTestFunc", "Test String argument from c++",
arg2);

        std::cout << "C++ : callPython() returned: " << retv << std::endl;
        std::cout << "C++ : test1ptr->Get() after Python call: " <<
test1ptr->Get() << std::endl;
  std::cout << "C++ : Press any key to quit" << std::endl;
  getch();
 }

 return 0;

} /* main */

Ok, and heres the full test_script.py file:
==========================
try:
     import grumsh
     print "Python: Import SUCCEEDED!!!"
except:
     print "Python: Import FAILED..."

############################################################################
#########
############################################################################
#########
def my_test(ptr):
 return 0;

############################################################################
#########
# Test module
# Illustrate both instatiation and use of already instantiated C++ objects
############################################################################
#########
def myTestFunc(msg, cppObj):

     # Print message from C++
     print "Python: msg = ", msg

     # Instantiate a MyClass object
     a = grumsh.TestC()

     # Show identity
     print "Python: Identity of 'local' MyClass object: ",
     a.printPtr()
     print

     # Change state of object
     print "Python: Current state:", a.Get()
     a.Set(1234)
     print "Python: New state :", a.Get()

     # Delete object
     del a

     # Test the object exposed from c++ (Passed as a formal parameter)
     # Show identify of cppObj
     print "Python: Identity of exposed cppObj: ",
     cppObj.printPtr()
     print

     # Show state of cppObj
     print "Python: cppObj.Get() on entry:", cppObj.Get()

     # Change state of object
     cppObj.Set(9)

     # Show state of object
     print "Python: cppObj.Get() on exit :", cppObj.Get()
     return 999


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk