Pass Python object directly to C++ program without using subprocess - python

I have a C++ program that, through the terminal, takes a text file as input and produces another text file. I'm executing this program from a Python script which first produces said text string, stores it to a file, runs the C++ program as a subprocess with the created file as input and parses the output text file back into a Python object.
Is it possible to do this without using a subprocess call? In other words: is it possible to avoid the reading and writing and just run the C++ program inside the Python environment with the text-string as input and then capture the output, again inside the Python environment?
For code I refer to the function community_detection_multiplex in this IPython notebook.

You can use ctypes.
It requires the C++ function to be wrapped with extern "c" and compiled as C code.
Say your C++ function looks like that:
char* changeString(char* someString)
{
// do something with your string
return someString;
}
You can call it from python like that:
import ctypes as ct
yourString = "somestring"
yourDLL = ct.CDLL("path/to/dll") # assign the dll to a variable
cppFunc = yourDLL.changeString # assign the cpp func to a variable
cppFunc.restype = ct.c_char_p # set the return type to a string
returnedString = cppfunc(yourString.encode('ascii')).decode()
Now returnedString will have the processed string.

Related

How can I pass variables from Python back to C++?

I have 2 files - a .cpp file and a .py file. I use system("python something.py"); to run the .py file and it has to get some input. How do I pass the input back to the .cpp file? I don't use the Python.h library, I have two separate files.
system() is a very blunt hammer and doesn't support much in the way of interaction between the parent and the child process.
If you want to pass information from the Python script back to the C++ parent process, I'd suggest having the python script print() to stdout the information you want to send back to C++, and have the C++ program parse the python script's stdout-output. system() won't let you do that, but you can use popen() instead, like this:
#include <stdio.h>
int main(int, char **)
{
FILE * fpIn = popen("python something.py", "r");
if (fpIn)
{
char buf[1024];
while(fgets(buf, sizeof(buf), fpIn))
{
printf("The python script printed: [%s]\n", buf);
// Code to parse out values from the text in (buf) could go here
}
pclose(fpIn); // note: be sure to call pclose(), *not* fclose()
}
else printf("Couldn't run python script!\n");
return 0;
}
If you want to get more elaborate than that, you'd probably need to embed a Python interpreter into your C++ program and then you'd be able to call the Python functions directly and get back their return values as Python objects, but that's a fairly major undertaking which I'm guessing you want to avoid.

Why captured sys.exit() code is different from the provided one?

I have a c++ program which runs a python program using system() command. I have return some value using the error code using sys.exit() in python code. But when I capture the returned value back in c++, it differs from one I coded in the python program.
My python code: test.py
import sys
sys.exit(10)
My c++ code: test.cpp
#include "iostream"
using namespace std;
int main ()
{
string str = "python test.py";
const char *command = str.c_str();
int value = system(command);
cout<<value;
return 0;
}
when I run run test.cpp, I got 2560
Why this happens?
The C++ standard defers to C (C++17 to C11) for this aspect, and it has this to say:
If the argument is a null pointer, the system function returns nonzero only if a
command processor is available. If the argument is not a null pointer, and the system function does return, it returns an implementation-defined value.
Assuming you're using Linux or some other POSIX-y type system, the return code matches that of the status that gets populated by the wait() call.
It's not a simple mapping, since it has to be able to return all sorts of meta-information about why the process exited, above and beyond the simple exit code (think of signals killing the process, for example).
That means you probably need to use the same macros you would for wait(), if you want to get the correct values.
Specifically, you should be using something like:
if (WIFEXITED(value))
printf("Normal exit, returned %d\n", WEXITSTATUS(value));
In your particular implementation, it probably shifts the return value eight bits left and uses the remaining bits to specify all those other useful things. That's an implementation detail so may not necessarily be correct but it's a good educated guess.
Just as paxdiablo said, it is indeed X*256 where X is the return code. Browsing through C++ documentation, the output of system MAY contain the error code, or may not, implementation defined:
Check: this and this. If you want to use the output and the return code on POSIX systems, you should be able to use wait

Python IDL bridges: Envi functions

my aim is to use a script written in IDL, into python:
IDL code:
PRO PS_GS
; Start the application
e = ENVI()
;Generate the roi from a vector file
; Open a vector file
file_vec = Filepath('Sic_Trapani.shp', ROOT_DIR = 'E:\mydirectory\')
vettore = e.OpenVector(file_vec)
; Get the task from the catalog of ENVITasks
Task_VtoR = ENVITask('VectorRecordsToROI')
; Define inputs
Task_VtoR.INPUT_VECTOR = vettore
; Define outputs
Task_VtoR.OUTPUT_ROI_URI = Filepath('roi_roi.xml', ROOT_DIR = 'E:\mydirectory\')
;Run the task
Task_VtoR.Execute
END
The above code, launched into IDL command prompt, works correctly.
I want make a python script that:
option 1) launch the above idl .pro script
option 2) use the IDL to Python Bridge sintax.
In the first case, using the subprocess.call("idldirectory\idl.exe") command, i can open the IDL prompt into the windows command prompt. But i can not execute any IDL function like a simple PRINT, 'hello'.
In the second case, i write the following poython code:
import subprocess
from subprocess import call
import idlpy
from idlpy import IDL
e=IDL.ENVI()
msi_file = """IDL.Filepath(mydata.tif", ROOT_DIR = 'mydirectory')"""
msi_raster = IDL.OpenRaster(msi_file)
The instruction e=IDL.ENVI() work correctly, in fact an Envi setion starts.
The instruction msi_file = """IDL.Filepath(mydata.tif", ROOT_DIR = 'mydirectory')""" work correctly.
My problem is with the OpenRaster instruction. It is an ENVI instruction and not an IDL instruction. So, IDL.OpenRaster does not work, and i do not have any solutions.
Can someone help me?
Thank you.
Lorenzo
You are halfway there. Where you went wrong was by calling the OpenRaster method as a static method on the IDL class. This isn't what you want to do. To use OpenRaster, you'll actually want to call that method on the ENVI object that you have created. For example:
e=IDL.ENVI()
msi_file = IDL.Filepath('mydata.tif', ROOT_DIR = 'mydirectory')
msi_raster = e.OpenRaster(msi_file)
Once you've created your object e, it behaves as any other python object. i.e. you can call it's methods, access properties etc. For example, to load your file into the ENVI display you could do the following:
view = e.GetView()
layer = view.CreateLayer(msi_raster)
The IDL class is just an interface that allows you to call any IDL function as a static method on the IDL class. But once you've instantiated an object, in this case e, use it as you would any other object.

Python C API - Reload a module

I use Python 3.4 and Visual 2010.
I'm embedding Python using the C API to give the user some script capabilities in processing his data. I call python functions defined by the user from my C++ code. I call specific function like Apply() for example that the user has to define in a Python file.
Suppose the user has a file test.py where he has defined a function Apply() that process some data.
All I have to do is to import his module and get a "pointer" to his python function from the C++.
PySys_SetPath(file_info.absolutePath().toUtf8().data()));
m_module = PyImport_ImportModule(module_name.toUtf8().data());
if (m_module)
{
m_apply_function = PyObject_GetAttrString(m_module, "Apply");
m_main_dict = PyModule_GetDict(m_module);
}
So far, so good. But if the user modifies his script, the new version of his function is never taken into account. I have to reboot my program to make it work... I read somewhere that I need to reload the module and get new pointers on functions but the PyImport_ReloadModule returns NULL with "Import error".
// .... code ....
// Reload the module
m_module = PyImport_ReloadModule(m_module);
Any ideas ?
Best regards,
Poukill
The answer was found in the comments of my first post (thank you J.F Sebastian), the PySys_SetPath has to contain also the PYTHONPATH. In my case, that is the reason why the PyImport_ReloadModule was failing.
QString sys_path = file_info.absolutePath() + ";" + "C:\\Python34\\Lib";
PySys_SetPath(UTF8ToWide(sys_path.toUtf8().data()));
m_module = PyImport_ReloadModule(m_module); // Ok !

Exchanging info between running C++ code and python script

I have a python script that is called inside a C++ program. The python script creates a directory based on the current time, puts files in there, and then execution returns to C++. I want to save a parameters file into the directory created in the python program.
I figure my options are:
Pass in the text to save in the parameters file to the Python program and have it create and save the file
Return the location of the directory to C++ from python so it knows where to save the file
Use C++ to locate the most recently created directory after execution of the python script and put file there
I'm not sure how to do any of this. My python script is not embedded. I use
std::string test = "python analyzeData2.py";
system(test.c_str());
to call a python script.
Any ideas how to do this?
I'd go with option B -- return the location of the directory to c++ from python so it knows where to save the file.
If you plan on using system(), something like this:
char* dirname[64];
FILE* fin;
system("python analyzeData2.py > created.log");
fin = fopen("created.log", "r");
fgets(dirname, sizeof(dirname), fin);
fclose(fin);
/* dirname has contents of created.log */
...

Categories

Resources