Call a Python function from c++ using boost.python - python

I have a python script script.py containing a function hello that simply prints Hello World. I would like to call this function in my c++ program using boost python module. Below is what I have done so far which is working fine and verifies my setup is working. Can someone please guide me how can I execute the function from within my c++ program contained in the python script ?
#include <boost/python.hpp>
#include <cstdlib> // setenv
int main()
{
// Allow Python to load modules from the current directory.
//setenv("PYTHONPATH", ".", 1);
// Initialize Python.
Py_Initialize();
namespace python = boost::python;
try
{
python::object main_module = python::import("__main__");
python::object main_namespace = main_module.attr("__dict__");
python::object print = python::exec("from time import time,ctime\n"
"print 'Today is',ctime(time())\n", main_namespace);
}
catch (const python::error_already_set&)
{
PyErr_Print();
return 1;
}
// Do not call Py_Finalize() with Boost.Python.
}

Related

Boost Python issue with converters - static linking

I have a question regarding the below code.
It's an example how to pass a custom class via shared_ptr to embedded python code and it works when boost is dynamically linked.
Unfortunately the same code with statically linked boost doesn't work with the following error message:
"No to_python (by-value) converter found for C++ type: class boost::shared_ptr".
I don't understand why a different linking can affect type recognition of a registered converter. What am I missing?
Can anybody help me out?
Thanks,
Dominik
Example from here.
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/python.hpp>
#include <string>
#include <iostream>
namespace bp = boost::python;
struct Foo{
Foo(){}
Foo(std::string const& s) : m_string(s){}
void doSomething() {
std::cout << "Foo:" << m_string << std::endl;
}
std::string m_string;
};
typedef boost::shared_ptr<Foo> foo_ptr;
BOOST_PYTHON_MODULE(hello)
{
bp::class_<Foo, foo_ptr>("Foo")
.def("doSomething", &Foo::doSomething)
;
};
int main(int argc, char **argv)
{
Py_Initialize();
try {
PyRun_SimpleString(
"a_foo = None\n"
"\n"
"def setup(a_foo_from_cxx):\n"
" print 'setup called with', a_foo_from_cxx\n"
" global a_foo\n"
" a_foo = a_foo_from_cxx\n"
"\n"
"def run():\n"
" a_foo.doSomething()\n"
"\n"
"print 'main module loaded'\n"
);
foo_ptr a_cxx_foo = boost::make_shared<Foo>("c++");
inithello();
bp::object main = bp::object(bp::handle<>(bp::borrowed(
PyImport_AddModule("__main__")
)));
// pass the reference to a_cxx_foo into python:
bp::object setup_func = main.attr("setup");
setup_func(a_cxx_foo);
// now run the python 'main' function
bp::object run_func = main.attr("run");
run_func();
}
catch (bp::error_already_set) {
PyErr_Print();
}
Py_Finalize();
return 0;
}
I far as I understand the documentation about Boost Python linkage, it seems that the conversion registry used for automatic conversion of Python object into C++ object is not available when statically linked. I'm facing the same issue and that's a pity it is actually the case. I would have imagined at least the required converter to be bundle but I'm afraid it is not the case for some reason.

embedding python in c++: python script not recognized

I am trying to embed python script into c++ project.
Below is what I have tried so far.
#include<iostream>
#include <Python.h>
int
main()
{
Py_Initialize();
PyObject* sysPath = PySys_GetObject("path");
PyObject* modPath = PyBytes_FromString("C:\\Users\\naal\\Documents\\Visual Studio 2017\\Projects\\Project1\pyscripts");
int result = PyList_Insert(sysPath,0, modPath);
PyObject *pModule = PyImport_ImportModule("myscript2");
printf("%p\n", pModule);
return 0;
}
below is the python script "myscript2.py"
def find_me():
print("hey you found me")
The problem is, the main module is not able to find the python script i.e object pyModule is always NULL, no matter how I change python script path.
What am I doing wrong ?
I ended up implementing this in another way.
#include<iostream>
#include <Python.h>
int main() {
std::string location = "C:\\Users\\myscript.py";
const char* file_location = location.c_str(); FILE* file_pointer;
Py_Initialize();
file_pointer = _Py_fopen(file_location, "r");
PyRun_SimpleFile(file_pointer, file_location);
Py_Finalize();
return 1;
}
The above seemed to work. I still don't know why the SYSPATH idea originially mentioned in the question didn't work.

PyImport_ImportModule always return NULL even though system path is set

I am trying to write a simple C/C++ program in Ubuntu 17.10 to call python function using embedding. However, it always complained that it is unable to import the module using PyImport_ImportModule.
I have included Python.h in the cpp file and compile it like this:
g++ -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu/ -g -o main main.cpp -I/usr/include/python3.6 -lpython3.6
I have tried the following things but none of the approach works:
add the current path to path using PySys_GetObject and PyList_Insert (as per this link)
set PYTHONPATH=/tmp then run the main program (as per this link)
/tmp/main.cpp
#include <iostream>
#include <python3.6/Python.h>
using namespace std;
int main()
{
Py_Initialize();
PyObject* sysPath = PySys_GetObject("path");
PyObject *path = PyBytes_FromString("/tmp");
int result = PyList_Append(sysPath, path);
PyObject* module = PyImport_ImportModule("hello");
if (0 != module)
{
PyObject* helloFunc = PyObject_GetAttrString(module, "hello");
if (helloFunc)
{
PyEval_CallObject(helloFunc, 0);
}
else
{
fprintf(stderr, "hello function is NULL");
}
}
else
{
fprintf(stderr, "hello module is NULL -- somehow");
}
Py_Finalize();
return 0;
}
/tmp/hello.py
class hello(object):
def __init__(self):
print("Hello world: init")
def hello(self):
print("Hello world: hello")
Please can someone tell me why PyImport_ImportModule always return NULL in my simple program?
Many thanks!

Creating instance with methods in C++ and passing it to Python

I'm trying to create an instance of Game, pass it into the main namespace of the test.py as the variable game, then call game.add(e) to run the C++ function that will add Entity e into the std::vector. However, this code produces the error:
unbound method Boost.Python.function object must be called with Game instance as first argument (got Entity instance instead)
(Some context: I'm trying to let Python create instances that will be kept in a container for C++ to run through every tick and update. I thought I had it working a few weeks ago but I came back to it and apparently it wasn't working - I know, source control.)
#include <vector>
class Entity{
public:
Entity(){}
Entity(float x, float y){}
};
class Game{
public:
Game();
void add(Entity* entity);
private:
std::vector<Entity*> objects_;
};
Game::Game(){
}
void Game::add(Entity* entity){
objects_.push_back(entity);
}
main.cpp:
#include <iostream>
#include <boost/python.hpp>
#include "Game.h"
#include "Entity.h"
using namespace boost::python;
BOOST_PYTHON_MODULE(sfgame){
class_<Game>("Game")
.def("add", &Game::add)
;
class_<Entity>("Entity", init<float, float>())
;
}
int main(){
PyImport_AppendInittab("sfgame", &initsfgame);
Py_Initialize();
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
import("sfgame");
Game* game = new Game();
try{
main_namespace["game"] = ptr(game);
exec_file("test.py", main_namespace);
}
catch (const boost::python::error_already_set &){
PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
std::string error;
error = boost::python::extract<std::string>(pvalue);
std::cout << error << std::endl;
}
delete game;
system("PAUSE");
return 0;
}
test.py:
from sfgame import *
e = Entity(5,5)
game.add(e)
You would receive that error if you set the variable name in the main namespace to Game, as it would be the same as the class name.
However, the code posted is correct. You must have compiled the .pyd file using the variable Game, realised your error, then changed it to game and compiled the C++ file to test it without recompiling the .pyd file so the error remained.

I can create my C++ object in Python, but can't access methods

Sorry, there's a lot here, but I really think all of this information is relevant to the question I'm about to ask.
So I wrapped my C++ class using SWIG, compiled, etc. and in a python script I create an instance of my class and then try to call a method on that class. Notice I said "try" to call. The call on the method fails. Details on implementation & "fails" follows.
First, I am building the Python3.4 interpreter with my Windows application and calling my python script as follows:
Py_Initialize();
/* Some code here to set up python paths. */
.....
PyObject *pName = PyUnicode_FromString("hello");
if(pName == NULL)
{
Py_Finalize();
return false;
}
PyObject* pModule = PyImport_Import(pName);
if(pModule == NULL)
{
Py_Finalize();
return false;
}
/* "go" is the name of the function in my python script that I want to call. */
PyObject *pFunc = PyObject_GetAttrString(pModule, "go");
if(pFunc == NULL)
{
Py_Finalize();
return false;
}
PyObject *pArgs = PyTuple_New(0);
PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
if(pValue == NULL)
{
Py_Finalize();
return false;
}
Py_Finalize();
return true;
===================
Here's my class structure:
class BoganDocument
{
private:
BoganMetadataSet myMetadata;
public:
// Constructor
BoganDocument();
// Destructor
virtual ~BoganDocument();
// Useful methods.
wstring getMetadataValue(wstring metadata_name);
}
===================
Here's my Python script (named "hello.py").
import BoganDocument
def go():
print("I'm in go()")
d = BoganDocument.BoganDocument()
print("I made a document")
ts = d.getMetadataValue("CreationTimestamp");
print("The creation timestamp is " + ts)
=====================
And the output on my console screen looks like this:
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4_d.pyd
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4.py
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4.pyw
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4.pyc
trying c:\MyApp\Workplace\swig_runtime_data4_d.pyd
trying c:\MyApp\Workplace\swig_runtime_data4.py
trying c:\MyApp\Workplace\swig_runtime_data4.pyw
trying c:\MyApp\Workplace\swig_runtime_data4.pyc
import 'BoganDocument' # <_frozen_importlib.SourceFileLoader object at 0x10D2E3F0>
import 'hello' # <_frozen_importlib.SourceFileLoader object at 0x10D2E1C0>
I'm in go()
I made a document
===================
Notice that I have set PYTHONVERBOSE to 11 to get as much diagnostic information as I can. And notice that all messages stop after "print("I made a document")". No syntax error, nothing. The value of pValue after "PyObject *pValue = PyObject_CallObject(pFunc, pArgs);" is NULL.
Now, the call to the constructor at "d = BoganDocument.BoganDocument()" is known to work, as I've set a breakpoint in the BoganDocument constructor and single stepped through.
I also set a breakbpoint in BoganDocument's getMetadataValue method but never get there. And there's the mystery.
No doubt I have a bug, but I haven't a clue.
Did you step into the SWIG wrapper for getMetadataValue? The C++ wrapper gets called when you do d.getMetadataValue() frm Python, the C++ wrapper then calls Bogan::getMetadataValue on the appropriate C++ instance. Since you never get to the C++ instance's getMetadataValue, maybe SWIG wrapper is finding the call to be illegal or such (SWIG doesn't normally silently ignore problems but I don't know what else to suggest). Since you are using wide string, make sure you include std_wstring.i.

Categories