问题
I'm trying to implement some Python stuff into my program and I've decided to use Boost::Python, so I compiled it according to the instructions, with bjam, using mingw/gcc, getting dlls and .a files
I'm using Code::Blocks for this, so I've put the dlls in the working directory of my project, where the rest of dlls I use are, and decided to run boost::python::exec("b = 5");
Instantly I get a crash. Ideas?
#include <boost/python.hpp>
float func(int a)
{
return a*a-0.5;
}
BOOST_PYTHON_MODULE(test_module)
{
using namespace boost::python;
def("func", func);
}
int main()
{
//Try one
boost::python::exec("b = 5");
//Crash
//Try two
Py_Initialize();
boost::python::exec("b = 5");
//Works fine
//Try three
Py_Initialize();
boost::python::exec("import test_module");
//Throws boost::python::error_already_set and crashes
/*
Something along the lines of
boost::python::exec("import test_module\n"
"var = test_module.func( 3 )\n");
*/
}
Under the build options section of my project, I've added libboost_python3-mgw48-d-1_54.dll
and libpython33
to be linked so it'd compile.
Ideas?
回答1:
When embedding Python, almost all calls to Python or Boost.Python should occur after the interpreter has been initialized with Py_Initialize(). Trying to invoke the interpreter before initialization, such as with boost::python::exec()
, will result in undefined behavior.
While that identifies the source of the crash, there are some subtle details to obtain the accomplish the final goal of embedding Python and a module, then have exec
import the embedded module.
- When importing a module, Python will first check if the module is a built-in module. If the module is not a built-in module, then Python will try to load a library based on the module name, and expects the library to provide a function that will initialize the module. As the
test_module
is being embedded, its initialization needs to be explicitly added so thatimport
can find it when searching for built-in modules. - The
import
statement uses the__import__
function. This function needs to be available withinexec
's globals.
Here is a complete example demonstrating how to accomplish this:
#include <boost/python.hpp>
float func(int a)
{
return a*a-0.5;
}
BOOST_PYTHON_MODULE(test_module)
{
using namespace boost::python;
def("func", func);
}
// Use macros to account for changes in Python 2 and 3:
// - Python's C API for embedding requires different naming conventions for
// module initialization functions.
// - The builtins module was renamed.
#if PY_VERSION_HEX >= 0x03000000
# define MODULE_INIT_FN(name) BOOST_PP_CAT(PyInit_, name)
# define PYTHON_BUILTINS "builtins"
#else
# define MODULE_INIT_FN(name) BOOST_PP_CAT(init, name)
# define PYTHON_BUILTINS "__builtin__"
#endif
int main()
{
// Add the test_module module to the list of built-in modules. This
// allows it to be imported with 'import test_module'.
PyImport_AppendInittab("test_module", &MODULE_INIT_FN(test_module));
Py_Initialize();
namespace python = boost::python;
try
{
// Create an empty dictionary that will function as a namespace.
python::dict ns;
// The 'import' statement depends on the __import__ function. Thus,
// to enable 'import' to function the context of 'exec', the builtins
// module needs to be within the namespace being used.
ns["__builtins__"] = python::import(PYTHON_BUILTINS);
// Execute code. Modifications to variables will be reflected in
// the ns.
python::exec("b = 5", ns);
std::cout << "b is " << python::extract<int>(ns["b"]) << std::endl;
// Execute code using the built-in test_module.
python::exec(
"import test_module\n"
"var = test_module.func(b)\n",
ns);
std::cout << "var is " << python::extract<float>(ns["var"]) << std::endl;
}
catch (python::error_already_set&)
{
PyErr_Print();
}
}
When executed, its output is:
b is 5
var is 24.5
来源:https://stackoverflow.com/questions/19794760/crash-on-call-from-boostpythonexec-anything