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.
I am using swig, which is wrapping one of my C++ function:
get(unsigned int a, unsigned int &b);
but i am failing to call this function from python, the error i get is TypeError that it should be unsigned int&
how call to this function from python ? should i add something special to the .i file ?
Yes,you should add typemaps.
example:
export.i:
%module my_mod
%include "typemaps.i"
%apply unsigned int &OUTPUT {unsigned int&}; //this map you output
%include "export.h"
export.h
void get(unsigned int a, unsigned int& b);
test
print(my_mod.get(10))
I'm writing a Python C extension by SWIG but getting frustrated when passing binary buffer to C api functions. Here's my example:
In utils.c
#include "utils.h"
void my_print_hexbytes(uint32_t *bytes, uint32_t bytes_len)
{
uint32_t i;
for(i = 0; i < bytes_len; i++)
printf("%02x", bytes[i]);
printf("\n");
}
In utils.h
#include "commons.h"
#ifndef XXXX_UTILS_H
#define XXXX_UTILS_H
void my_print_hexbytes(uint32_t *bytes, uint32_t bytes_len);
#endif /* XXXX_UTILS_H */
In commons.h
#ifndef XXXX_COMMONS_H
#define XXXX_COMMONS_H
....
....
#include <stdint.h>
....
....
#endif
In xxxx_for_py.i
%module xxxx_for_py
%{
#define SWIG_FILE_WITH_INIT
#include "commons.h"
#include "utils.h"
%}
%include "stdint.i"
void my_print_hexbytes(uint32_t *bytes, uint32_t bytes_len);
1st Try
After compiling out _xxxx_for_py.so, I gave a try testing it in IPython:
In [1]: import xxxx_for_py
In [2]: from ctypes import *
In [3]: a_t = c_uint * 2
In [4]: a = a_t(0x1, 0x2)
In [5]: xxxx_for_py.my_print_hexbytes(a, 2)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-3c023fcf8b04> in <module>()
----> 1 xxxx_for_py.my_print_hexbytes(a, 2)
TypeError: in method 'my_print_hexbytes', argument 1 of type 'uint32_t *'
That time I had no idea how to handle this case. I tried changing the a as bytearray but it didn't work.
Not sure if anyone who could help me on this issue. Thanks!
2nd Try
Thanks for #Petesh comment. I've hit a try by casting a to POINTER(c_uint) but still not work :(
In [5]: xxxx_for_py.my_print_hexbytes(cast(a, POINTER(c_uint)), 2)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-2abb9dae484d> in <module>()
----> 1 xxxx_for_py.my_print_hexbytes(cast(a, POINTER(c_uint)), 2)
TypeError: in method 'my_print_hexbytes', argument 1 of type 'uint32_t *'
3rd Try
Referenced by Unbounded Array section in SWIG documentation, I added carray.i to xxxx_for_py.i
In xxxx_for_py.i
....
....stdint.i"
%include "carrays.i"
%array_class(uint32_t, uint32Array)
....
After re-compiling and loading .so, it still got same error
In [1]: import xxxx_for_py
In [2]: a = xxxx_for_py.uint32Array(2)
In [3]: a[0] = 0x0
In [4]: a[1] = 0x1
In [5]: xxxx_for_py.my_print_hexbytes(a, 2)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-3c023fcf8b04> in <module>()
----> 1 xxxx_for_py.my_print_hexbytes(a, 2)
TypeError: in method 'my_print_hexbytes', argument 1 of type 'uint32_t *'
4th Try
After a week later at that time, user Nethanel post an answer. Though it still did not work, it could help me find something interesting.
After brief testing, I found solution by myself. See this article for details http://au9ustine.github.io/wiki/crypto/cuda/swig.html#typeerror-in-method-xxxx-argument-y-of-type-zzzz (Update on 2017-01-13: This page is broken, I'll move that page content here)
A brief anatomy
Keeping trying on testing Python codes on passing binary buffer to a trivial C module (i.e. library compiled by gcc without extra dependencies). And internals might look like:
integer list -> array/struct -> raw string -> C function parameter
From some descriptions from create_string_buffer. By create_string_buffer, Python could provide such a feature dynamically allocating pieces of memory for current variables for use.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
size_t
print_int_buf(void *src, size_t len)
{
size_t i = 0;
uint32_t *int_ptr = (uint32_t *)src;
for (i = 0; i < len; i++) {
printf("%p: %x\n", int_ptr + i, int_ptr[i]);
}
return i;
}
If compiled as a shared library named my_ext_lib.so, the code could be loaded in Python
import ctypes
from ctypes import *
import array
my_ext = ctypes.CDLL('./my_ext_lib.so')
buf = create_string_buffer(array.array('I', range(0x10)).tostring())
my_ext_lib.print_int_buf(buf, 0x10)
And it did work.
Though ctype and SWIG are quite different approaches for
interoperating integration between Python and C, I use ctype code
here to elaborate the basic mechanism and its working way of passing
parameters to a compiled module (both for Python module and trivial
C shared library module).
Interim solution
Since the significant difference was the function interface (or method signature) from my view, why not change it to void * instead of uint32_t *? (Though it's not the best way, it could be still an interim solution for containment)
I replaced uint32_t * with void * for the method my_print_hexbytes, and appended dependency cdata.i (I'm not sure if it works but for safety it has been added). So the changes were listed below and eventually it displayed expected result.
utils.c
#include "utils.h"
...
void
my_print_hexbytes(void *bytes, size_t bytes_len)
{
size_t i = 0;
uint32_t *int_buf_ptr = (uint32_t *)bytes;
for(i = 0; i < bytes_len; i++)
printf("%02x", int_buf_ptr[i]);
printf("\n");
}
header utils.h
#include "commons.h"
#ifndef XXXX_UTILS_H
#define XXXX_UTILS_H
...
void my_print_hexbytes(void *bytes, size_t bytes_len);
#endif /* XXXX_UTILS_H */
xxxx_for_py.i
%module xxxx_for_py
%{
#define SWIG_FILE_WITH_INIT
#include "commons.h"
#include "utils.h"
%}
%include "stdint.i"
%include "carrays.i"
%include "cdata.i"
%array_class(uint32_t, uint32Array)
...
void my_print_hexbytes(void *bytes, size_t bytes_len);
Invoking method in Python, it would be like:
In [1]: import xxxx_for_py
In [2]: a = xxxx_for_py.uint32Array(0x10)
In [3]: for i in range(0x10):
...: a[i] = i
...:
In [4]: xxxx_for_py.my_print_hexbytes(a, 0x10)
000102030405060708090a0b0c0d0e0f
I had a very simple case but still got this error. The fix from this question worked:
Swig python - c++ how to use type int8_t
I added %include "stdint.i" directly after the module def in the .i file
Found you question, because of same problem.
Unfortunately, it seems advanced types are not supported by SWIG: in http://www.swig.org/Doc3.0/Library.html#Library_carrays, after description of array_class macro, it is written: "When using this macro, type is restricted to a simple type name like int or float."
I also tried unsuccessfully to use "unsigned int".
In the end my workaround was to use C++ vectors instead of uint32_t pointers, here is relevant part of swig file:
%include "stdint.i"
%include "std_vector.i"
namespace std {
%template(vectoruint32) vector<uint32_t>;
};
part of h file:
#include <vector>
static uint32_t foo(std::vector<uint32_t> v) {return v[1] - v[0];}
python:
import example
l = [1, 2, 3, 4]
example.foo(l)
Hope this helps
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];
}
}
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!