[C++-sig] Re: Contributing an embedding example (after a bugfix ;)
thor.arne.johansen at ibas.no
thor.arne.johansen at ibas.no
Wed Dec 4 17:18:25 CET 2002
>thor.arne.johansen at ibas.no writes:
>
>
[Snip]
>
>> I'm wondering if it is appropriate to post the example here for review
or a
>> (reasonable?) example of embedding with Boost.Python. The example is
about
>> 350 lines (including the test script)?
>
>Sure! Maybe you and Dirk can collaborate over what makes a good
>embedding example. Two heads are better than one, as they say.
>
>You might consider getting Jeremy Siek to set you guys up with write
>access to the Boost CVS sandbox, so you can develop your code/docs
>there. However, if that's too much mental overhead, doing it on this
>list should be OK.
I think I will stay out of the sandbox for now :), but I'm posting my
example code below. The hope of course is that I will get some useful
comments/tips (on error/exception handling??), and that the example might
be useful to others:
***** Begin: main.cpp *****
//
// Embedding and extending with Boost.Python example
// By Thor Arne Johansen
//
#include <iostream>
#include <string>
#include <cstdio>
#include <conio.h>
#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>
// Error codes
#define Success 0
#define PY_InitError -100
#define PY_ConvertError -101
#define PY_CallError -102
// Global module pointer
boost::python::handle<> module;
// Class definitions
//
// PythonException: Exception object thrown by Python interface (usually
caused by an exception in Python interpreter)
//
class PythonException
{
std::string m_exception_type;
std::string m_error_message;
public:
PythonException():m_exception_type(""),m_error_message(""){};
void setExceptionType(std::string msg) {m_exception_type = msg;}
void setErrorMessage(std::string msg) {m_error_message = msg;}
std::string getExceptionType() {return m_exception_type;}
std::string getErrorMessage(void) {return m_error_message;}
};
//
// Test class which contain state
//
class MyClass
{
private:
_int64 m_state;
public:
MyClass():m_state(0){}
MyClass(_int64 st):m_state(st){}
void setState(_int64 st) { m_state = st;}
_int64 getState() { return m_state;}
void printPtr() { printf("C++ : Ptr=0x%p", this); }
};
// Function prototypes
int initPython(std::string scriptName);
int callPython(std::string func, std::string msg,
boost::python::object& obj);
void getExceptionDetail(PythonException& exc);
// 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)
{
PythonException exc;
getExceptionDetail(exc);
throw(exc);
}
return handle<>(module_ptr);
}
boost::python::object
get_function(const char *func_name)
{
PyObject* dict = PyModule_GetDict(module.get()); // Borrowed
reference
boost::python::str funcName(func_name);
return boost::python::object(boost::python::handle
<>(PyDict_GetItem(dict, funcName.ptr())));
}
// Python wrapper for MyClass
BOOST_PYTHON_MODULE(myCppModule)
{
using namespace boost::python;
class_<MyClass>("MyClass")
.def("getState", &MyClass::getState)
.def("setState", &MyClass::setState)
.def("printPtr", &MyClass::printPtr)
;
}
// 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 retv;
PythonException p_exc; // Create empty exception object on
stack
try
{
boost::python::object func = get_function(const_cast<char
*>(funcStr.c_str()));
retv = boost::python::extract<int>(func(msg, obj));
}
catch(...) // What should we catch here??
{
getExceptionDetail(p_exc);
throw(p_exc);
}
return retv;
}
//
//
// 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("myCppModule", initmyCppModule);
// Initialize the Python interpreter
Py_Initialize();
// Set up so sys.path contain our script path
std::string pyinitstr(
"import sys\n"
"sys.path.append(\"##SCRIPTPATH##\")\n"
"\n"
);
// Build full path to scripts
// For now, we hardcode the path where the scripts are located
// Typically this would be read from a configuration file
std::string scr_path = "d:/work/ExtendEmbedd2";
// Insert path into init string
std::string placeHolder("##SCRIPTPATH##");
unsigned int pos = pyinitstr.find(placeHolder);
if(pos!=std::string::npos)
{
pyinitstr.erase(pos, placeHolder.length());
pyinitstr.insert(pos, scr_path);
}
// Run the init script to set up sys.path
if( PyRun_SimpleString(const_cast<char*>(pyinitstr.c_str())) != NULL )
std::cout << "C++ : Warning: Could not set up sys.path\n" <<
std::endl;
// Import the module, storing the pointer in a handle<>
try
{
module = import_module(const_cast<char*>(scriptName.c_str()));
}
catch(PythonException& exc)
{
std::cout << "\nC++ : Exception thrown when importing module in
initPython():" << std::endl;
std::cout << "C++ : " << exc.getExceptionType() << ": " <<
exc.getErrorMessage() << std::endl;
return PY_InitError;
}
catch(...)
{
std::cout << "C++ : Unknown exception when importing module " <<
scriptName << std::endl;
return PY_InitError;
}
return Success;
}
// Complete the exception object exc based on the exception thrown by the
Python module
// This was adopted from pyerrors.c found here:
http://rmi.net/~lutz/errata-supplements.html
void getExceptionDetail(PythonException& exc)
{
PyObject* exc_type;
PyObject* exc_value;
PyObject* exc_traceback;
PyObject* pystring;
PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
if( exc_type==0 && exc_value==0 && exc_traceback==0)
{
exc.setExceptionType("Strange: No Python exception occured");
exc.setErrorMessage("Strange: Nothing to report");
}
else
{
pystring = NULL;
if (exc_type != NULL &&
(pystring = PyObject_Str(exc_type)) != NULL && /*
str(object) */
(PyString_Check(pystring))
)
exc.setExceptionType(PyString_AsString(pystring));
else
exc.setExceptionType("<unknown exception type>");
Py_XDECREF(pystring);
pystring = NULL;
if (exc_value != NULL &&
(pystring = PyObject_Str(exc_value)) != NULL && /*
str(object) */
(PyString_Check(pystring))
)
exc.setErrorMessage(PyString_AsString(pystring));
else
exc.setErrorMessage("<unknown exception data>");
Py_XDECREF(pystring);
Py_XDECREF(exc_type);
Py_XDECREF(exc_value); /* caller owns all 3 */
Py_XDECREF(exc_traceback); /* already NULL'd out */
}
}
//
// Initialize, and run some tests
//
int main(int argc, char *argv[])
{
std::cout << "C++ : Initializing Python and loading script" <<
std::endl;
int retv = initPython("myscript");
if( retv == Success )
{
// Create a MyClass, and set some state information
MyClass mc(10);
// Show identity
mc.printPtr(); std::cout << std::endl;
// Show state
std::cout << "C++ : mc.getState() in C++ before Python call: "
<< mc.getState() << std::endl;
// Do some stuff to the object from Python
// First wrap a reference to mc in an bpl object
boost::python::object arg2(boost::ref(mc));
// Show identity of the wrapped object
arg2.attr("printPtr")();std::cout << std::endl;
// Alternative method to do the same thing
boost::python::call_method<void>(arg2.ptr(),"printPtr"); std::cout
<< std::endl;
try
{
// Call Python, possibly changing the state of mc
int retv = callPython("myTestFunc", "String argument from c++",
arg2);
std::cout << "C++ : callPython() returned: " << retv <<
std::endl;
}
catch(PythonException& exc)
{
std::cout << "\nC++ : Exception thrown in callPython():" <<
std::endl;
std::cout << "C++ : " << exc.getExceptionType() << ": "
<< exc.getErrorMessage() << std::endl;
retv = PY_CallError;
}
catch(...)
{
std::cout << "C++ : Unknown exception when calling
callPython!" << std::endl;
retv = PY_CallError;
}
// Show state
std::cout << "C++ : mc.getState() after Python call: " <<
mc.getState() << std::endl;
}
else
std::cout << "C++ : Failed to initialize!" << std::endl;
std::cout << "C++ : Press any key to quit" << std::endl;
getch();
return retv;
}
***** End: main.cpp *****
***** Begin: myscript.py *****
try:
import myCppModule
print "Python: Import SUCCEEDED!!!"
except:
print "Python: Import FAILED..."
#
# 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 = myCppModule.MyClass()
# Show identity
print "Python: Identity of 'local' MyClass object: ",
a.printPtr()
print
# Change state of object
print "Python: Current state:", a.getState()
a.setState(1234)
print "Python: New state :", a.getState()
# 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.getState() on entry:", cppObj.getState()
# Change state of object
cppObj.setState(-9223000000000000000) # Test the limits of
long long (or _int64 on msvc)
# Show state of object
print "Python: cppObj.getState() on exit :", cppObj.getState()
return 999
***** End: myscript.py *****
--
Thor Arne Johansen
Dept. Manager R&D
Ibas AS
More information about the Cplusplus-sig
mailing list