Sign in to follow this  
sipickles

Getting started with boost::python - Exception already!

Recommended Posts

Hi, I am trying to embed python with boost::python. Unfortunately, I've come unstuck already. Here's my code
	object main_module( ( handle<>( borrowed( PyImport_AddModule( "__main__" ) ) ) ) );

	object main_namespace = main_module.attr("__dict__");

	FILE* myPython = fopen( "myPython.py", "r" );

	handle<> ignored( ( PyRun_File( myPython, "myPython.py",
						Py_file_input, 
						main_namespace.ptr(), 
						main_namespace.ptr() ) ) );

	fclose( myPython );

and heres the exception: First-chance exception at 0x7c918fea in Server 20070601.exe: 0xC0000005: Access violation writing location 0x00000010. Unhandled exception at 0x7c918fea in Server 20070601.exe: 0xC0000005: Access violation writing location 0x00000010. What am I doing wrong? must be something simple :) Thanks! Simon EDIT: some info may help.... MSVC++ 2005 Express Boost 1.34 Python 2.52 [Edited by - sipickles on June 1, 2007 1:15:06 PM]

Share this post


Link to post
Share on other sites
I've managed to make a little progress by using boost::python::exec_file, now available in boost 1.34.

Now I struggle to extract anything from a python function apart from std::string and int.

Here's what I am trying to do:

// C++
python::dict global;
python::object result = python::exec_file("myPython.py", global, global)l

python::object greet = global["greet"];
std::string message = python::extract<std::string>(greet());


// MYPYTHON.PY
def greet(self):
return "hello, world!"




The extract attempt gives 'error_already_set' exception.

Help!

Share this post


Link to post
Share on other sites
If you run that stand-alone Python code, you'll see that it doesn't work, and raises an exception. Hint: that's not a function you've defined, that's a method.

(PS. Also copy and paste in your actual code, rather than typing it out separately, so that we can see if there are syntax errors that may contribute to the problem.)

Share this post


Link to post
Share on other sites
Yes, you are right. I was foolishly copying code from the boost.org tutorials

Oh well! Guess I'll have to keep twiddling til I randomly hit upon the solution!

Here's the cut and paste code:

	try
{
// Run a python script in an empty environment.
python::dict global;
python::object result = python::exec_file("myPython.py", global, global);

int i= 0;
// Extract an object the script stored in the global dictionary.

i = python::extract<int>(global["number"]);
g_log->Log("success!! %d\n", i);

std::string animal = python::extract<std::string>(global["animal"]);
g_log->Log("animal == %s\n", animal.c_str());

// Create a reference to it.
python::object greet = global["greet"];

// Call it.
std::string message = python::extract<std::string>(greet);
g_log->Log("%s\n", message.c_str());
}
catch(python::error_already_set)
{
g_log->SYSTEM( "Python Error\n" );
}




number = 42
animal = "aardvark"

def greet():
return animal


it excepts on the line:

python::object greet = global["greet"];

I guess this means there is no greet object in the python global namespace...

Share this post


Link to post
Share on other sites
It could mean anything. There'll be a way to get hold of that exception and find out what it means, so that would be worth looking into.

Share this post


Link to post
Share on other sites
Though you seem to have gotten around this, you still might want to know. For some reason, PyRun_File can't take a regular FILE* for it's argument. I can't remember the actual reason. It's something about Python wanting one format of FILE but Windows has a different one. Put simply, you can't use PyRun_File. I'm not at home right now, but when I am, I'll try looking for where I found this.

Share this post


Link to post
Share on other sites
Yeah, it depends whether the library was built with the same compiler as you are using. You can rebuild from source to overcome this, apparently!

Share this post


Link to post
Share on other sites
Well, the solution turned out to be simple. Not sure I TOTALLY understand why, its like I had to instantiate a function, then extract the object produced:

	try
{
// Run a python script in an empty environment.
python::dict global;
python::object result = python::exec_file("myPython.py", global, global);

int i= 0;
// Extract an object the script stored in the global dictionary.

i = python::extract<int>(global["number"]);
g_log->Log("number = %d\n", i);

std::string animal = python::extract<std::string>(global["animal"]);
g_log->Log("string == %s\n", animal.c_str());


// Call it.
std::string message = python::extract<std::string>(global["passedFunc"]);
g_log->Log("%s\n", message.c_str());
}
catch(python::error_already_set)
{
g_log->SYSTEM( "Python Error\n" );
}





number = 42
animal = "aardvark"

def pythonFunc():
return "Hello from Python"

passedFunc = pythonFunc()



Bingo! :P

Share this post


Link to post
Share on other sites
Glad to hear that you got it working. You might also want to ask future questions on the C++-SIG at Python.org. There's a number of people right there that are very skilled with Boost.Python and love to answer your questions. In general, I've gotten better responses from there than I have from GD.net.

TBH, I've just resorted to running a PyRun_SimpleString() that imports files. It's become easier than mucking about with the other stuff.

Share this post


Link to post
Share on other sites
I notice you're just printing out a generic error on error_already_set exceptions, but you can figure out what Boost::Python's throwing exceptions about by catching the exception like this:


try
{
....
}
catch(boost::python::error_already_set)
{
PyErr_Print();
throw;
}


Share this post


Link to post
Share on other sites
Hi BigJim,

My problem is I have a win32 app, so no console output. Anyway I can output PyErr to a std::string so I can send it to my logger?

Share this post


Link to post
Share on other sites
Here's the code that I use to do it. You should be able to figure out what to do pretty easily from this. Basically, you make a logging function (or two, one for errors, one for regular prints). Then you make a light class wrapping the function in Python. Then you tell python to use that instead of the default. Feel free to use any/all of this code.

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

//Error logging code:
void PrintString(char* str);
BOOST_PYTHON_MODULE(Log)
{
def("LogString",&PrintString);
}

void InitPythonLogging()
{
initLog();

PyRun_SimpleString("import sys");
PyRun_SimpleString("import Log");
PyRun_SimpleString("class StdOutRedirector:\n def write( Self, String ):\n Log.LogString(String)");
PyRun_SimpleString("sys.stdout = StdOutRedirector()");
PyRun_SimpleString("sys.stderr = StdOutRedirector()");
}

void PrintString(char* str)
{
std::cout << str;
std::cout.flush();
}


This method has some limitation that I don't like. For example, Python gives you errors line-by-line. I would have loved a way to get the entire error in one string. Then I could, for example, have a pop-up box alerting me of the error in debug mode. But, there's no easy way to differentiate between two errors AFAIK.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this