Using typedef with SWIG - python

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!

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"

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.
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.

Understanding repetitions in Swig tutorial example

I am new to learning swig. I am interested in calling C++ from Python on an Ubuntu machine.
I just started looking at the intro tutorial here http://www.swig.org/tutorial.html
Consider the interface file on that page example.i copied as is below.
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
%}
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
Why is are the contents between the %{ %} repeated in the second half of the file? As given in the manual, http://www.swig.org/Doc3.0/SWIGDocumentation.html#Introduction_nn5
The %{ %} block provides a location for inserting additional code,
such as C header files or additional C declarations, into the
generated C wrapper code.
But it doesn't address the point of the repetition in the example. What am I missing?
The code between %{ and %} is inserted verbatim in the generated SWIG wrapper, and is used to give the wrapper code access to the headers or declarations listed.
The code outside those markers instructs SWIG to make a wrapper for each of the declarations (or entire header files) listed.
If you left out extern int fact(int n); in the first portion, the wrapper, when compiled and linked to the source or library containing the function, wouldn't be able to access the function since the extern declaration would be missing. If left out of the second portion, a wrapper wouldn't be generated to access it from the scripting language.
There is a shortcut:
%inline %{
...
%}
That instructs SWIG to both insert and wrap the declarations.

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];
}
}

Categories