How to import boost module from C++ to python? - python

Following is the c++ code which I want to import.
#include <boost/python.hpp>
#include <string>
/*
* This is the C++ function we write and want to expose to Python.
*/
const std::string hello() {
return std::string("hello, zoo");
}
/*
* This is a macro Boost.Python provides to signify a Python extension module.
*/
BOOST_PYTHON_MODULE(zoo) {
// An established convention for using boost.python.
using namespace boost::python;
// Expose the function hello().
def("hello", hello);
}
The following code is python script.
import zoo # In zoo.cpp we expose hello() function, and it now exists
in the zoo module.
assert 'hello' in dir(zoo) # zoo.hello is a callable.
assert callable(zoo.hello) # Call the C++ hello() function from Python.
print zoo.hello()
When I try to run the script, I am not getting "hello, zoo" at the terminal as output. Where am I making the mistake?
Following is the error message I am getting:
import: not authorized zoo' # error/constitute.c/WriteImage/1028.
./visit_zoo.py: line 3: syntax error near unexpected token('
./visit_zoo.py: line 3: `assert 'hello' in dir(zoo)'

Aren't you forgetting to indicate the script should be run by Python, as I did?
You can either include the python executable in the header of your script file:
#!/usr/bin/env python2
and make the file executable or call the script with Python:
$ python <filename>

Related

Using pybind11 within C++ in JUCE Framework

I'm trying to read a string list output from a Python script, in order to pass it to a JUCE MainComponent (written in C++).
The code is the following (which is a starting one, just to test the pybind library):
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
namespace py = pybind11;
MainComponent::MainComponent()
{
auto math = py::module::import("math");
auto resultobj = math.attr("sqrt")(2);
double result = resultobj.cast<double>();
}
However, I always encounter the following error:
_PyRuntime.gc.**generation0** was nullptr.
Any suggestions? Thanks in advance
You have to first initialize the Python interpreter, like this:
MainComponent::MainComponent()
{
py::scoped_interpreter guard{};
auto math = py::module::import("math");
auto resultobj = math.attr("sqrt")(2);
double result = resultobj.cast<double>();
}
You also have to link with the needed python libraries. In CMake, this means adding
target_link_libraries(your_target PRIVATE pybind11::embed)
to your CMakeLists.txt file.

Call function from python in C++

I'm currently working on a project with a separate cli and gui. The gui part is written in C++ to keep the exectuable small. I compiled my python program with pyinstaller and need to call some functions from the C++ program.
Python part:
import configparser
def getconfig() -> list:
config = configparser.ConfigParser()
config.read("config.ini")
section1 = config["general"]
section2 = section["section2"]
return [section1, section2] #should be a list of dict?
Compiling it via pyinstaller --onefile --clean myprogram.py
What i would like to do in C++ is :
//please correct me if this is the wrong type,
//i know i probably need to do a bit of parsing between python and C++ types
std::vector<std::unordered_map<std::string, std::string>> > = myprogram.getconfig()
I just don't want to do the config parsing in C++ again, or would you recommend this as it's probably easier to call the compiled python binary?
This program just needs to run on linux, windows is not necessary.
If you are only planning to run on POSIX-compatible systems (ie. GNU/Linux), you could spawn a new process to run the Python script, e.g. using the C function exec. Example:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main()
{
char* args[] = { "pythonscript.py", NULL };
int t = execv("pythonscript.py", args);
if(t < 0) printf("%s\n", strerror(errno));
return 0;
}
And then in pythonscript.py:
#!/usr/bin/python3
print("hello, world!")
This trick works with bash scripts as well.
The script must be executable though, so remember to run chmod +x pythonscript.py.
EDIT:
You will likely need to use pipes or other mechanisms for inter-process communication. (this is not my expertise!)

How can I save a python function to a static c++ container using pybind11?

Essentially, on the C++ side I have a container that holds a certain type of function. Now I would like to expose this container to python with the possibility for the users to provide their own python functions.
The simplest example would look like this:
#include "pybind/common/Common.h"
using CppFunc = std::function< int (int) >;
PYBIND11_MODULE( test, m )
{
m.def("addFunc", [](const pybind11::function& f){
static std::vector<CppFunc> vec{};
vec.push_back(f.cast<CppFunc>());
});
}
Then in python I would like to just do something like this.
import test
def myFunc(number):
return number+1
test.addFunc(myFunc)
Interestingly enough, this works fine. However, if I run the script with "python script.py" it runs through and then never terminates. In an interactive console, the same code works fine until you try to close the console: the process gets stuck.
How can I safely store this python function in the C++ container?
static std::vector<CppFunc> vec{} stores references to python objects (user functions) which never get released due to static storage, therefore interpreter cannot terminate.
To ensure interpreter termination you can call a cleanup function at module termination:
#include "pybind11/pybind11.h"
#include "pybind11/functional.h"
namespace py = pybind11;
using CppFunc = std::function< int (int) >;
PYBIND11_MODULE( test , m )
{
static std::vector<CppFunc> vec{};
m.def("addFunc", [](CppFunc f){
vec.push_back(std::move(f));
});
m.add_object("_cleanup", py::capsule([]{ vec.clear(); }));
}
See doc for more details: https://pybind11.readthedocs.io/en/stable/advanced/misc.html#module-destructors

Python script calling a C function, which calls a Python function

I'm a regular C user, but pretty new to Python.
I have a library written in C for performing calculations that I'm trying to make callable from a Python script. The library needs some user defined routines, for which I am trying to allow the use of Python scripts.
I am running into a problem which demonstrates to me that I do not understand something very fundamental. Here is the code for a simple program I cannot get to run. It should print to the screen the result of 7 (2+5).
The Python script test.py is called first. It loads ctypes and the library libfoo.so, and calls the C routine c_do_work:
from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')
print "Python 1: Going in..."
lib.c_do_work(2,5)
The C function c_do_work is defined in the library libfoo.so, which has the single module test.c. This routine should run the Python script my_func.py, which defines the function find_sum. A Python interpreter is initialized here:
//gcc test.c -I/usr/include/python2.7/ -L/usr/lib/python2.7/ -lpython2.7 -lm -fPIC -c
//gcc -shared -Wl,-soname,libfoo.so -o libfoo.so test.o
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
void c_do_work(int a,int b)
{
Py_Initialize();
PyObject* main_module = PyImport_AddModule("__main__");
PyObject* main_dict = PyModule_GetDict(main_module);
FILE* file_1 = fopen("my_func.py", "r");
PyRun_File(file_1, "my_func.py",Py_file_input,main_dict, main_dict);
PyObject* expression = PyDict_GetItemString(main_dict,"find_sum");
printf("C: calling Python function...\n");
PyObject_CallFunction(expression,"ii",a,b);
//Clean up
fclose(file_1);
Py_Finalize();
}
Lastly, the Python script my_func.py:
def find_sum(a, b):
print a+b
When I run "python test.py", I get a segmentation fault at the second line of the C function:
PyObject* main_module = PyImport_AddModule("__main__");
Why is this happening? If I slightly rewrite the C routine so it is main, and run that program directly, I get the desired result. The problem seems to be related to having a Python interpreter calling a routine that initializes a Python interpreter.

PyObject_GetAttrString C++ function returning NULL: Unable to call Python functions from C++

I've just started working with Python with C++ and I'm a bit confused on why I'm unable to call functions in Python from C++.
Here is my current test code in C++:
#include <iostream>
#include <Python.h>
using namespace std;
int main()
{
Py_Initialize();
PyObject* myModuleString = PyString_FromString("test");
PyObject* myModule = PyImport_Import(myModuleString);
if( myModule )
{
PyObject* myFunction = PyObject_GetAttrString(myModule, "Hello");
if( myFunction )
{
PyEval_CallObject( myFunction, NULL );
}
else
{
fprintf( stderr, "myFunction is NULL" );
}
}
else
{
fprintf( stderr, "myModule is NULL" );
}
Py_Finalize();
cin.get();
return 0;
}
Here is my test.py Python code:
import sys
def Hello():
print "Hello, world!"
Before I had a more complicated test, but I ran into an issue where PyObject_GetAttrString passed back NULL, so I wanted to make a simpler test and I still received NULL after calling PyObject_GetAttrString. From my understanding PyObject_GetAttrString gets you the PyObject* to the function and then I call it after, so receiving NULL there basically means I can't call the function.
Also yes I have looked at https://docs.python.org/2.7/ and even tested the example given in step 5.3 of https://docs.python.org/release/2.6.5/extending/embedding.html#pure-embedding (I'm using 2.7.7 because I'm planning to integrate with 3ds Max ). It still runs into the same issues with PyObject_GetAttrString.
I'm assuming it's a simple mistake or step I'm missing in the C++ or Python code.
Changing the Python script name from test.py to anything else worked for me.
I was having a similar problem. I found that the whitespace in the string I was passing to python was way off. From what I could tell, your print statement has 6 spaces in lieu of 4. Back it up and see if everything doesn't clear up.
I had the same problem. Although test.py and mycode.c were in the same folder (/home/user/python/example), I had to add a reference to the path after Py_Initialize(); as shown in following line
PyRun_SimpleString ("import sys; sys.path.insert(0, '/home/user/python/spp')");*
Replace /home... to your path.
My previous code did not have that line of code but PyImport_Importwas working and PyObject_GetAttrString wasn't. Makes no sense to me, "don't ask me, I don't know - Ozzy".

Categories

Resources