Salman Rahman
Salman Rahman

Reputation: 41

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

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.

Upvotes: 2

Views: 945

Answers (1)

Chris Warkentin
Chris Warkentin

Reputation: 131

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.

Upvotes: 1

Related Questions