Getting a char* output from C++ to Python in SWIG - python

I am trying to create a Python Bluetooth wrapper to wrap C++ classes. This is my SWIG interface file:
%module blsdk
%include "pyabc.i"
%include "std_vector.i"
%include "cstring.i"
%include "cpointer.i"
%include "typemaps.i"
%include serialport.i
%include exploresearch.i
Here is my serialport.i
%module serialport
%{
#include <string>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <termios.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <assert.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
#include <bluetooth/rfcomm.h>
#include "BTSerialPortBinding.h"
%}
%include "BTSerialPortBinding.h"
My BTSerialPortBinding.h has these functions:
static BTSerialPortBinding *Create(std::string address, int channelID);
int Connect();
void Close();
int Read(char *buffer, int length);
void Write(const char *write_buffer, int length);
bool IsDataAvailable();
How can I wrap the int Read(char* buffer, int length) function? I want to have the char* buffer as output and the length as input. I have tried to define the read function as int Read(char* OUTPUT, int length) but this gives an error: TypeError: a bytes-like object is required, not 'str' in my program as I need a byte object in Python. Any help would be very much appreciated.

This is not a complete answer but it might serve to get you started hacking around. As always with SWIG the key is looking at the generated code and monkeying around with it. Writing off the top of my head so again, just a starting point.
One thing you can do is a bit hacky but could work if you have some theoretical limit to how much data you read. A handy way is to 'swallow' the input and return value with a pair like this:
%typemap(in,numinputs=0) char *buffer
{
$1 = malloc(some_arbitrary_large_amount);
// or 'cheat' by looking at swig output and using the value you just happen
// to know is the length (like arg1 or something)
}
%typemap(argout) char *buffer
{
PyObject *tmp = $result;
int len = 0;
int res = SWIG_AsVal_long(tmp, &len);
if(!SWIG_IsOK(res)) {
free($1);
SWIG_fail;
}
$result = SWIG_From_CharPtrAndSize( $1, len );
PyDecRef(tmp); //probably?
free($1);
}
This would change the interface on the python side to just take the length argument and return a python string which may not be what you want. Note that you can return whatever you like so instead of SWIG_From_CharPtr you could create some other python object like a bytearray.
Another approach is to play with multi-arg typemaps. More hazy on the details here but you'd be doing something like:
%typemap(in) (char *buffer, int length)
{
/*
$input is a python object of your choice - bytearray?
Use the various Python/Swig APIs to decode the input object.
Set $1 and $2 to the data pointer and length decoded from
your input object and they will be passed to the C function.
*/
}
Now you have a Read() function on the python side that takes one argument which is up to you to create and set the size of. Could be anything as long as you can figure out how to get access to the internal array and size. Numpy is a pretty good candidate but if you're using Numpy, they already have a really nice set of typemaps for SWIG. Then you'd just do:
%include "numpy.i"
%apply( char *IN_ARRAY1, int DIM1 )
and give it a numpy array.

Related

Accessing C nested structs in Python after interfacing with SWIG

Preface:
I have two header files: "Sample.h" and "Sample2.h". Here are the contents of the two headers:
"Sample.h" has:
#include "Sample2.h"
typedef struct {
int c;
sJustFloats sJf;
}sTest;
"Sample2.h" has:
typedef struct {
float a;
float b;
}sJustFloats;
Sample.c just has:
#include "Sample.h"
My SWIG interface file has:
%module Sample
%{
/* Put header files here or function declarations like below */
#include "Sample.h"
%}
%include "Sample.h"
%apply float {float a}; // For sJustFloats.a
%apply float {float b}; // For sJustFloats.b
%apply int {int c}; // For sTest.c
%apply sJustFloats {sJustFloats sJf}; // For sTest.sJf
%apply sTest {sTest test}; //I guess this exposes test struct itself.
Steps followed to get .so file that is imported in Python:
gcc -c -fPIC Sample.c Sample_wrap.c -I /usr/include/python3.6m
ld -shared Sample.o Sample_wrap.o -o _Sample.so
Observations:
I am able to import this as a module in Python and I can access fields of sTest as follows:
test = Sample.sTest()
test.c // To access "c" -- This works fine
Problem: I am not able to access fields of struct sJustFloats
If I do test.sJf(), it gives me TypeError: 'SwigPyObject' object is not callable. Similarly test.sJf.a gives: AttributeError: 'SwigPyObject' object has no attribute 'a'
Can someone please tell me how to access members of the struct, sJustFloats?
Additional observations:
When I do type(Sample.sTest), it gives me <class 'type'>, whereas doing type(Sample.sTest.sJf) gives me <class 'property'>.
If I have both structs in the same file "Sample.h", I am able to access all the fields by doing:
test = Sample.sTest()
sjf = test.sJf()
sjf.a # Works fine
sjf.b # Works fine
Am I missing something in the interface file? Are there are nuances of SWIG that I'm unaware of?
SWIG doesn’t recurse by default, but you need %include "Sample2.h" after %include "Sample.h" and don’t need any of the %apply:
%module Sample
%{
#include "Sample.h"
%}
%include "Sample.h"
%include "Sample2.h"

Swig error- Error: Syntax error in input(2)

I have been trying to create a python wrapper for a c++ library. Swig is giving an error for this section of code, and I don't understand what is causing the error. Also I am new to using swig. The error it is giving me is "Syntax error in input(2)", also Here is the section of code.
typedef void (__cdecl *TSI_FUNCTION_CAMERA_CONTROL_CALLBACK) (int ctl_event, void*context);
typedef void (__cdecl *TSI_FUNCTION_CAMERA_CONTROL_CALLBACK_EX) (int ctl_event, TSI_FUNCTION_CAMERA_CONTROL_INFO *ctl_event_info, void *context);
typedef void (__cdecl *TSI_FUNCTION_IMAGE_NOTIFICATION_CALLBACK) (int notification, void *context);
typedef void (__cdecl *TSI_FUNCTION_IMAGE_CALLBACK) (TsiImage *tsi_image, void *context);
typedef void (__cdecl *TSI_TEXT_CALLBACK_FUNCTION) (char *str, void *context);
SWIG does not understand __cdecl. You possibly need to add `%include "windows.i" to your interface file. You may have other symbols, which are undefined, but from the information that you have given I cannot tell.
%module example
%{
#include "example_if.h"
%}
...
# Here it must be present
%include "windows.i"
...
%include "example_if.h"

How to extend c++ class in python with operator [], using swig

I have a simple test.h file with my own array class (which uses the standard vector class):
#include <vector>
#include <string>
using namespace std;
class Array1D{
private:
vector<double> data_;
int xsize_;
public:
Array1D(): xsize_(0) {};
// creates vector of size nx and sets each element to t
Array1D(const int& nx, const double& t): xsize_(nx) {
data_.resize(xsize_, t);
}
double& operator()(int i) {return data_[i];}
const double& operator[](int i) const {return data_[i];}
};
I want to be able to use the [] operator in python using swig. My current SWIG interface file looks like
%module test
%{
#define SWIG_FILE_WITH_INIT
#include "test.h"
%}
%include "std_vector.i"
namespace std{
%template(DoubleVector) vector<double>;
}
%include "test.h"
When I make the module, everything runs fine, but when I instantiate an object of Array1D, a = test.Array1D(10,2), which creates a length 10 vector with 2 in each element, and type a[1] I get
TypeError: 'Array1D' object does not support indexing.
How should my SWIG interface file look in order to extend the operator method so I can output a[1] properly within python? I would also like to be able to do something like a[1] = 3.0;
I figured it out. This is what I needed to add to my interface file:
%extend Array1D{
const double& __getitem__(int i) {
return (*self)[i];
}
}

Using typedef with SWIG

I have a C header file containing structure definitions with typedef, and an array of char definition with typedef too:
/* File: test.h */
typedef struct
{
char *key;
void *value;
int size;
} cti_pair;
typedef char FOO[CONST];
The SWIG interface file contains the following lines:
/* File: test.i */
%module test
%{
#define SWIG_FILE_WITH_INIT
#include "/home/users/jdoe/workspace/project/src/lib-cti/test.h"
...
%}
#ifndef CTI_TYPES_H_
#define CTI_TYPES_H_
#include "cti_const.h"
typedef char FOO[CONST];
typedef struct
{
char *key;
void *value;
int size;
} cti_pair;
The problem is that I can access the cti_pair struct, but can't use FOO (it's not defined) in my python script.
Ok, just figure out what the problem was.
Actually, I used %extend in another part of the interface file. But this keyword can only be used on structures, not on basic typedef (and SWIG does not display any error message if we use %extend the wrong way).
As a matter of fact, FOO was defined. The problem was not related to this at all. Thanks!

%rename and %inline in swig, error checking

I use SWIG and Numpy. I define a C function called inplace() to process data array fast, and I want to make some error checking (if two arrays have the same dimentions).
I use %rename and %inline in the .i file. As I understand, rename should map the function names', so every time someone uses inplace, safe_inplace is run and the errors are checked.
But it does not work :( . As far I notice, safe_inplace is not executed, python runs directly inplace without touching the safe version of the function.
# .i
%include "inplace.h"
%rename (inplace) safe_inplace;
%inline %{
void safe_inplace(int* datain, int in_dx, int in_dy, int in_dz,
int* dataout, int out_dx, int out_dy)
{
if ((in_dx != out_dx) || (in_dy != out_dy)) {
PyErr_Format(PyExc_ValueError, /*... messgage*/)
return;
}
inplace( /* .. pass the arguments to original function*/ );
}
header file:
# .h
void inplace(int* datain, int in_dx, int in_dy, int in_dz, int* dataout, int out_dx, int out_dy);
Python:
#.py
inplace.inplace(a,b)
The original example that I modify can be found here
Your original approach was close, but you probably want to get SWIG not to wrap the original version of the function at all.
I've put together a slightly simpler example illustrating how this could work. Given the header file:
void foo();
We want to wrap it such that a slightly modified version is called before invoking the real one.
The simplest way to do this would be to never actually show SWIG the header file at all for wrapping, only for compiling the wrapper, e.g.:
%module test
%{
#include "test.h"
#include <iostream>
%}
%rename (foo) foo_safe;
%inline %{
void foo_safe() {
std::cout << "Hello world" << std::endl;
foo(); // Calls the foo() from test.h, as you'd hope
}
%}
If you don't want to drop the %include (e.g. there's other things you care about in that header file too) you can do something like:
%module test
%{
#include "test.h"
#include <iostream>
%}
%rename (unsafe_foo) foo;
%include "test.h"
%rename (foo) foo_safe;
%inline %{
void foo_safe() {
std::cout << "Hello world" << std::endl;
foo();
}
%}
To expose the real implementation of foo as unsafe_foo.
Or you could use %ignore if there's no reason for Python users to be able to call unsafe_ignore ever:
%module test
%{
#include "test.h"
#include <iostream>
%}
%rename (foo) foo_safe;
%inline %{
void foo_safe() {
std::cout << "Hello world" << std::endl;
foo();
}
%}
%ignore foo;
%include "test.h"
Finally it looks like your goal is actually just to run some code prior to the call of the real C function. If that's the case there's a few ways you can do that too, for example you can add python code before the real function call is made with pythonprepend:
%feature("pythonprepend") foo() %{
print "hello world"
# check args and raise possibly
%}
Or lastly you could use the %exception functionality too, something like (untested):
%exception inplace {
// Check args and possibly throw
$action
}
$action will be substituted with the real call automatically.

Categories