Python callback from C++ - python

I have a C++ class
class EventHandler {
virtual long readFromDaemon(void *buf, size_t count) = 0;
};
and a Python program that uses it
class Handler(EventHandler):
def readFromDaemon(self, buf, count):
...
C++ code calls EventHandler::readFromDaemon().
SWIG converts arguments and calls Python's readFromDaemon():
long SwigDirector_EventHandler::readFromDaemon(void *buf, size_t count) {
...
swig::SwigVar_PyObject obj0;
obj0 = SWIG_NewPointerObj(SWIG_as_voidptr(buf), SWIGTYPE_p_void, 0 );
swig::SwigVar_PyObject obj1;
obj1 = SWIG_From_size_t(static_cast< size_t >(count));
...
swig::SwigVar_PyObject swig_method_name = SWIG_Python_str_FromChar((char *)"readFromDaemon");
swig::SwigVar_PyObject result = PyObject_CallMethodObjArgs(swig_get_self(), (PyObject *) swig_method_name ,(PyObject *)obj0,(PyObject *)obj1, NULL);
I want to convert (void *buf, size_t count) to PyBytesObject
def readFromDaemon(self, buf):
# buf is bytes
But I didn't found a way how to achieve it.
%typemap(in) (void *buf, size_t count) { ... } does not help.

Here's an example of reading and writing to C++ data buffers. the C++ functions in my example are:
void read(void* buf, size_t count);
void write(const void* buf, size_t count);
SWIG interface file:
%module x
%include <exception.i>
// Handle an input-only (const) buffer.
// A single Python input is translated into a buffer and a size.
%typemap(in) (const void *buf, size_t count) (Py_ssize_t tmp) %{
if(PyBytes_AsStringAndSize($input,(char**)&$1,&tmp) == -1)
return NULL;
$2 = tmp;
%}
// Handle an output-only (non-const) buffer.
// The single integer Python input is allocated in a temporary
// buffer and the pointer and its size are created as C++ parameters.
%typemap(in,numinputs=1) (void *buf, size_t count) (long tmp) %{
if(!PyLong_Check($input))
SWIG_exception(SWIG_TypeError,"expected integer");
tmp = PyLong_AsLong($input);
if(tmp < 1 || tmp > 65535)
SWIG_exception(SWIG_ValueError,"expected value 1-65535");
$2 = tmp;
$1 = new char[$2];
%}
// The pair of output arguments are translated into a single
// Python bytes object and appended to any existing return value.
%typemap(argout) (void *buf, size_t count) (PyObject* po) %{
po = PyBytes_FromStringAndSize((char*)$1,$2);
$result = SWIG_Python_AppendOutput($result,po);
%}
// Free the temporary buffer created in the "in" typemap.
%typemap(freearg) (void* buf, size_t count) %{
delete $1;
%}
// SWIG will wrap these two functions prototypes.
void read(void* buf, size_t count);
void write(const void* buf, size_t count);
// Implementation
%{
#include <iostream>
void write(const void* buf, size_t count)
{
char* tmp = (char*)buf;
for(size_t i = 0; i < count; i++)
std::cout << i << ": " << (int)tmp[i] << std::endl;
}
void read(const void* buf, size_t count)
{
char* tmp = (char*)buf;
for(size_t i = 0; i < count; i++)
tmp[i] = (char)i;
}
%}
Demo:
>>> import x
>>> x.read(10)
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'
>>> x.read(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: expected value 1-65535
>>> x.read(1000000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: expected value 1-65535
>>> x.write(b'abcdefg')
0: 97
1: 98
2: 99
3: 100
4: 101
5: 102
6: 103
>>> x.write(12)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected bytes, int found

I found a decision in another topic here.
%typemap(directorin) (void *buf, size_t count) {
$input = PyBytes_FromStringAndSize(static_cast<char*>($1), $2);
}
But there is another problem. The memory buffer is copied from C++ to Python object. How to copy bytes back from python object to C++? There is memoryview since python 2.7, but what to do with 2.6 and olders versions?
This is C++ code that calls EventHandler::readFromDaemon, for clarity
std::vector<char> buf(maxlen);
long cnt = m_evt_handler->readFromDaemon(&buf[0], buf.size());
Here is wrapper code now
long SwigDirector_EventHandler::readFromDaemon(void *buf, size_t count) {
long c_result;
swig::SwigVar_PyObject obj0;
{
obj0 = PyBytes_FromStringAndSize(static_cast<char*>(buf), count);
}
...

Related

Python Method Call in a C++ Code through Python C-API

I am working on my Project which implies the use of Empirical Mode Decomposition in C++ for EEG Signals. The input Data is Eigen::MatrixXd, where the rows are the Channels and the columns are the samples.
I did not found a good C++ library for EMD so I want to use a Python one (dsatools). I have downloaded the package through Pip installer from the setup.py file on Xubuntu... so it's a system package now.
the problem is that the program can't read the module.
this is the code:
std::vector <Eigen::MatrixXd> DataAquisition::EMD (Eigen::MatrixXd array, int order, int iterations, int locality) {
std::vector <Eigen::MatrixXd> IMFs;
for (int i = 0; i < array.rows(); i++) {
Eigen::MatrixXd Kanals = array.row(i);
Eigen::MatrixXd IMFs_Cpp;
Py_Initialize();
//PyRun_SimpleString("from dsatools._base._imf_decomposition import * ");
PyObject* sys_path = PySys_GetObject("path");
PyObject* ProgrammName = PyUnicode_FromString("/home/user/Schreibtisch/mne-cpp-main/applications/mne_bci/MNE-BCI-QT/dsatools-master/dsatools/_base/_imf_decomposition/_emd.py");
PyList_Append(sys_path, ProgrammName);
PyObject* pModuleString = PyUnicode_FromString ((char*)"_emd.py");
PyObject* pModule = PyImport_Import(pModuleString);
PyObject* pFunction = PyObject_GetAttrString(pModule,(char*)"emd");
//PyObject* pDict = PyModule_GetDict(pModule);
//PyObject* pFunc = PyDict_GetItemString(pDict, (char*)"emd");
if (PyCallable_Check(pFunction))
{
PyObject* Signal = Py_BuildValue("(d)",(double*)Kanals.data());
PyObject* Order = Py_BuildValue("(i)",order);
PyObject* method = Py_BuildValue("(z)",(char*)"cubic");
PyObject* max_itter = Py_BuildValue("(i)",iterations);
PyObject* args = PyTuple_Pack(4,Signal,Order,method,max_itter);
PyErr_Print();
PyObject* IMFs_Py = PyObject_CallObject(pFunction,args);
PyErr_Print();
if (PyArray_Check(IMFs_Py))
std::cout << "EMD Output is NOT Array \n";
PyArrayObject *np_ret = reinterpret_cast <PyArrayObject*> (IMFs_Py);
int Rows = PyArray_SHAPE(np_ret)[0];
int Cols = PyArray_SHAPE(np_ret)[1];
double* c_out = reinterpret_cast<double*>(PyArray_DATA(np_ret));
Eigen::MatrixXd IMFs_Cpp = Eigen::Map <Eigen::MatrixXd> (c_out,Rows,Cols);
IMFs.push_back(IMFs_Cpp);
}
else
std::cout << "Python did not call the function \n";
Py_Finalize();
}
return IMFs;}
this is how the code in Python should look like and I just want to call the emd function:

Map data in C++ to memory and read data in Python

I am mapping integers to memory in C++ (Process 1) and trying to read them in Python (Process 2) ..
Current Results:
1) map integer 3 in C++ ==> Python (b'\x03\x00\x00\x00')
2) map integer 4 in C++ ==> Python (b'\x04\x00\x00\x00'), and so on ..
code:
Process 1
#include <windows.h>
#include <iostream>
using namespace std;
void main()
{
auto name = "new";
auto size = 4;
HANDLE hSharedMemory = CreateFileMapping(NULL, NULL, PAGE_READWRITE, NULL, size, name);
auto pMemory = (int*)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, NULL, NULL, size);
for (int i = 0; i < 10; i++)
{
* pMemory = i;
cout << i << endl;
Sleep(1000);
}
UnmapViewOfFile(pMemory);
CloseHandle(hSharedMemory);
}
Process 2
import time
import mmap
bufSize = 4
FILENAME = 'new'
for i in range(10):
data = mmap.mmap(0, bufSize, tagname=FILENAME, access=mmap.ACCESS_READ)
dataRead = data.read(bufSize)
print(dataRead)
time.sleep(1)
However, my goal is to map an array that is 320*240 in size but when I try a simple array as below
int arr[4] = {1,2,3,4};
and attempt to map to memory by * pMemory = arr;
I am getting the error "a value of type int* cannot be assigned to an entity of type int" and error code "0x80070002" ..
Any ideas on how to solve this problem??
P.S for some reason integer 9 is mapped as b'\t\x00\x00\x00' in python ==> what am I missing?
Use memcpy to copy the array to shared memory.
#include <cstring>
#include <windows.h>
int main() {
int array[320*240];
const int size = sizeof(array);
const char *name = "new";
HANDLE hSharedMemory = CreateFileMapping(NULL, NULL, PAGE_READWRITE, NULL, size, name);
void *pMemory = MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, NULL, NULL, size);
std::memcpy(pMemory, array, size);
UnmapViewOfFile(pMemory);
CloseHandle(hSharedMemory);
}

ctypes Errors with argv

I'm trying to call in my C ++ library from python with types but I can't pass as IP arguments and HEX code, can anyone help me?
import sys
import ctypes
lib = ctypes.CDLL('./hello.so')
LP_c_char = ctypes.POINTER(ctypes.c_char)
LP_LP_c_char = ctypes.POINTER(LP_c_char)
lib.connect_pe_func.argtypes = (ctypes.c_int, LP_LP_c_char)
argc = 2
argv = ["192.168.2.170","2600000026"]
for i, arg in enumerate(sys.argv):
enc_arg = arg.encode('utf-8')
argv[i] = ctypes.create_string_buffer(enc_arg)
lib.connect_pe_func(argc, argv)
this is the error message, how do I insert IP and Hex code into the argv vector without having this error?
---------------------------------------------------------------------------
ArgumentError Traceback (most recent call last)
<ipython-input-21-f59eabe02690> in <module>
17 argv[i] = ctypes.create_string_buffer(enc_arg)
18
---> 19 lib.connect_pe_func(argc, argv)
ArgumentError: argument 2: <class 'TypeError'>: expected LP_LP_c_char instance instead of list
for completeness I also insert the C ++ code that I compiled and made into a hello.so library
#include <stdio.h>
#include <errno.h>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <unistd.h>
#include <sstream>
#include "connect_PE_func.h"
using namespace std;
extern "C" char * connect_pe_func(int argc, char *argv[])
{
int sockfd, n;
int connected = 0;
struct sockaddr_in servaddr;
std::string serveraddr = argv[1];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
servaddr.sin_port = htons(9761);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
std::string pref_hex;
std::string hex("0x");
std::string test = argv[2];
size_t numbytes = test.size() / 2;
uint8_t command[numbytes];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
pref_hex = hex + test.substr(x, 2);
cout << pref_hex;
command[w] = stoi(pref_hex, nullptr, 16);
}
int bytes_to_send = sizeof(command);
send(sockfd, command, bytes_to_send, 0);
uint8_t output_command[numbytes];
recv(sockfd, output_command, bytes_to_send, 0);
char test_out[10];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
test_out[x] = (char)output_command[w];
}
return test_out;
};
extern "C" char * hello_world(char * name){
char * output = (char *) calloc(sizeof(name)+7, sizeof(char));
strcat(output, "Hello ");
strcat(output, name);
strcat(output, "\0");
//output[sizeof(output)-1] = '/0';
return output;
};
Python lists can't be passed as pointer of pointers.
To create and populate a pointer of pointers from Python, you need:
to create a pointer array: p = (LP_c_char*len(argv))() (allocates the space for the pointers)
to cast it into a pointer of pointers: na = ctypes.cast(p, LP_LP_c_char) (makes it compatible with the decayed pointer of pointers form)
or without the redefinitions:
p = (ctypes.POINTER(ctypes.c_char)*len(argv))()
na = ctypes.cast(p, ctypes.POINTER(ctypes.POINTER(ctypes.c_char)))
Fixed code:
import sys
import ctypes
lib = ctypes.CDLL('./hello.so')
LP_c_char = ctypes.POINTER(ctypes.c_char)
LP_LP_c_char = ctypes.POINTER(LP_c_char)
lib.connect_pe_func.argtypes = (ctypes.c_int, LP_LP_c_char)
argv = ["192.168.2.170","2600000026"]
argc = len(argv)
p = (LP_c_char*len(argv))()
for i, arg in enumerate(argv): # not sys.argv, but argv!!!
enc_arg = arg.encode('utf-8')
p[i] = ctypes.create_string_buffer(enc_arg)
na = ctypes.cast(p, LP_LP_c_char)
lib.connect_pe_func(argc, na)
To test this, I've created a very simple c++ code (instead of yours)
#include <stdio.h>
extern "C" char * connect_pe_func(int argc, char *argv[])
{
for (int i=0;i<argc;i++)
{
puts(argv[i]);
}
return 0;
}
built with: g++ -shared -o hello.so test.cpp
running the python module proves that arguments are passed all right:
192.168.2.170
2600000026
Inspired by Pointers and arrays in Python ctypes

Error in method argv[] SWIG C++ / Python, what happens?

I'm using swig to connect a C ++ function with my Python libraries. I managed to compile everything and create the .so file. But after having done the import of my C ++ function in python I have some errors on the argv ... maybe I'm wrong to pass the values to it from the outside or maybe when I compiled and created the .i swig file I got something wrong. The C ++ function creates a TCP socket and sends a Hex code to a device that replies with another Hex code, from a terminal with the C ++ code i write:
connect_PE_func 192.168.1.170 600000060
and it works perfectly. and it works perfectly. I would expect a similar syntax once I set my c ++ function via swig in python, type:
answer= connect_PE_func.connect_PE_func("192.168.1.170", 600000060,2)
but I get this error:
test=connect_PE_func.connect_pe_func(["192.168.2.170"],["2600000026"])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-f58f79b27fe5> in <module>
----> 1 test=connect_PE_func.connect_pe_func(["192.168.2.170"],["2600000026"])
~/mqtt_atenapy/C_connect_PE_dev/test_cpython/connect_PE_func.py in connect_pe_func(argv, argc)
64
65 def connect_pe_func(argv, argc):
---> 66 return _connect_PE_func.connect_pe_func(argv, argc)
67
68
TypeError: in method 'connect_pe_func', argument 1 of type 'char *[]'
I attach the code to the files .c .h e .i which I used with swig to get the .so.
thanks.
connect_PE.func.c :
#include <stdio.h>
#include <errno.h>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <unistd.h>
#include <sstream>
#include "connect_PE_func.h"
using namespace std;
// to compile gcc connect_PE_func.cpp -lstdc++ -c
char* connect_pe_func(char *argv[],int argc)
{
int sockfd, n;
int connected = 0;
struct sockaddr_in servaddr;
std::string serveraddr = argv[1];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
servaddr.sin_port = htons(9761);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
std::string pref_hex;
std::string hex("0x");
std::string test = argv[2];
size_t numbytes = test.size() / 2;
uint8_t command[numbytes];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
pref_hex = hex + test.substr(x, 2);
cout << pref_hex;
command[w] = stoi(pref_hex, nullptr, 16);
//cout << test.substr(x, 2);
//cout << "\n";
//cout << command[w];
//cout << "\n";
}
//uint8_t command[] = {0x26, 0x00, 0x00, 0x00, 0x26};
int bytes_to_send = sizeof(command);
send(sockfd, command, bytes_to_send, 0);
uint8_t output_command[numbytes];
recv(sockfd, output_command, bytes_to_send, 0);
char test_out[10];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
test_out[x] = (char)output_command[w];
//cout << unsigned(test_out[x]);
}
return test_out;
}
connect_PE_func.h:
// file: connect_PE_func.h
char* connect_pe_func(char *argv[], int argc);
connect_PE_func.i:
/* file: connect_PE_func.i */
%module connect_PE_func
%{
/* Everything in this block will be copied in the wrapper file. We include the C header file necessary to compile the interface
*/
#include "connect_PE_func.h"
// extern char *connect_pe_func(int argc, char *argv[]);
%}
/* list functions to be interfaced: */
char* connect_pe_func(char *argv[], int argc);
You've misplaced a comma func(["192.168.2.170"] , ["2600000026"])
My guess is that you need to pass func(["192.168.2.170", "2600000026"], 2)

Passing OpenCv Mat from C++ to Python

I need to send an OpenCv image from C++ to Python to do some processing on it.
The Mat will be received through the code but for simplicity I am using imread here for the question.
What I did in the C++ part of the code was:
#include <Python.h>
#include <arrayobject.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Mat image = imread("test.jpg");
Py_Initialize();
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pValue;
pName = PyUnicode_FromString("prog");
if (pName == NULL)
{
PyErr_Print();
return 0;
}
pModule = PyImport_Import(pName);
if (pModule == NULL)
{
PyErr_Print();
return 0;
}
pDict = PyModule_GetDict(pModule);
pFunc = PyDict_GetItemString(pDict, "add");
if (pFunc == NULL)
{
PyErr_Print();
return 0;
}
pArgs = PyTuple_New(1);
import_array ();
npy_intp dimensions[3] = {image.rows, image.cols, image.channels()};
pValue = PyArray_SimpleNewFromData(image.dims + 1, (npy_intp*)&dimensions, NPY_UINT8, image.data);
PyTuple_SetItem(pArgs, 0, pValue);
PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
if(pResult == NULL)
cout<<"Calling the add method failed"<<endl;
long result = PyLong_AsLong(pResult);
cout<<"Result = "<<result<<endl;
Py_Finalize();
return 0;
}
This code compiles and runs.
For the Python part:
import cv2
import numpy as np
def add (a):
print ("Contents of a :")
print (a)
# mat_array = cv2.fromarray(a, numpy.float32)
vis0 = cv.fromarray(a)
return 0
The Python code receives the numpy array from C++ (I think) and when I print the contents of a, I have an output (so I think I am receiving the image from C++).
Now I need to convert the data in a to a cv2 Mat in Python so that I can work on it.
Once I reach the mat_array = cv2.fromarray(a, numpy.float32) line or vis0 = cv.fromarray(a) the code crashes with the following output:
Exception ignored in: <module 'threading' from '/usr/lib/python3.5/threading.py'>
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 1283, in _shutdown
assert tlock.locked()
SystemError: <built-in method locked of _thread.lock object at 0x7ff0f34d20d0> returned a result with an error set
How do I correctly send / receive the Mat object?
Please find my answer here. You can also find other answer here. for converting numpy -> cv::Mat and cv::Mat -> numpy.

Categories