Use SWIG to apply multiple Java data types for same C data type - swig

I have two C functions that I'm exposing through SWIG to my Java layer and both have an input param with a const void * data type ("val) that needs to be a uint8_t for the addCategory function but a char for the addAttribute function. I'm currently, in the SWIG Interface file, using the %apply to map the const void * C type to a short on the Java side. Is there a way to modify the SWIG interface file to support both a char (String) and a uint8_t (short) for the const void * input parameter?
C Functions from header file:
int
addCategory(query_t *query, type_t type, const void *val);
int
addAttribute(query_t *query, type_t type, const void *val);
SWIG Interface File:
%module Example
%include "stdint.i"
void setPhy_idx(uint32_t value);
%include "arrays_java.i"
void setId(unsigned char *value);
%{
#include "Example.h"
%}
%apply char * { unsigned char * };
%apply char * { void * };
%apply uint8_t { const void * }
%apply int32_t { int32_t * }
%include "Example.h"

You can't directly do this - what type would be used in this place in Java? You need to help SWIG decide that in some way.
You have (at least) three possible solutions:
Use a type hierarchy - The base type will be what the function takes, the subclasses will get wrapped also. You could do this on the C++ side, or on the Java side using SWIG's typemap facilities. I think this is needlessly complicated though, so I've not made an example here.
Use overloads (or even different functions, with different names altogether - you could use %rename to make them back into overloads in Java even if they have different names in C)
Use a union. This will get wrapped with set and get functions by SWIG:
%module test
union values {
unsigned char *string;
void *generic;
uint8_t someOtherThing;
uint32_t number;
};
void func(values v);
This results in a Java class called values, which func() takes and can pass one of the members of the union through. Clearly you'd want to %apply appropriate typemaps for the members of the union.

Related

How to change the default code generated by SWIG for the allocation of memory for a C structure?

I am using a flexible array in the structure. So I want to change the memory allocated for that structure with some of my own code. Basically I want to change the new_structname() and structname_variable_set() functions.
typedef struct vector{
int x;
char y;
int arr[0];
} vector;
here, SWIG generated new_vector() function to allocate memory by calling calloc(1,sizeof(struct vector)) where swig will not handle these type of structure in a special manner. So we need to modify the swig generated new_vector() in order to allocate memory for the flexible array. So is there any way to handle this?
There are a few ways you can do this. What you're looking for though is %extend. That lets us define new constructors and implement them as we see fit. (It even works with a C compiler, they're only constructors from the perspective of the target language).
Using your vector as a starting point we can illustrate this:
%module test
%include <stdint.i>
%inline %{
typedef struct vector{ int x; char y; int arr[0]; }vector;
%}
%extend vector {
vector(const size_t len) {
vector *v = calloc(1, sizeof *v + len);
v->x = len;
return v;
}
}
With this SWIG synthesises a new_vector function in the generated module code as you'd hoped.
I also assumed that you want to record the length inside the struct as one of its members. If that's not the case you can simply delete the assignment I made.

SWIG, not able to create typemap for long*

I have a C function like this one:
int make_call(char* to, int *call_id);
and a very simple interface file:
%include "typemaps.i"
%include "enums.swg"
%include "arrays_java.i"
%header %{
#include <my_header.h>
%}
/* Map "int*" as input & output */
%apply int *INOUT { int * };
This works just fine. In my SWIG file I get:
public static int make_call(String to, int[] call_id) {..}
and I can call it creating an int[] with one element. All good so far.
However as the call_id can be quite large it needs to be a long so I changed this in the C API and I added to the .i file this line:
%apply long *OUTPUT { long * };
I have tried several variants including trying to apply what is said in the java.html doc for SWIG but I always end up with an int[] beeing generated or maybe the swigtype for long that I am not able to use from Java. I am just not able to make it generate a long[] that I am expecting.
I tried just making it a double like this:
%apply double *OUTPUT { double * };
and it works as expected and I get my double[] call_id in the generated interface.
Is there any pitfalls with the long type? It would seem wierd right?
Try one of:
unsigned long
unsigned int
long long

How can I use templates or typedefs to select between float/double vector types?

A usual way to target different floating point precisions (float / double) is either by typedefs
typedef float Real;
//typedef double Real;
or by using templates
template<typename Real>
...
This is convenient, but anyone has ideas how to use the CUDA types float2/float3/... and make_float2/make_float3/... ? Sure, I could make #defines or typedefs for all of them but that seems not very elegant.
You can implement helper class that will concatenate type and channels number:
template <typename T, int cn> struct MakeVec;
template <> struct MakeVec<float, 3>
{
typedef float3 type;
};
template <> struct MakeVec<double, 3>
{
typedef double3 type;
};
// and so on for all combination of T and cn
Usage:
template <typename T>
void func()
{
typedef typename MakeVec<T, 4>::type vec4_type;
vec4_type vec4; // for T=float it will be float4, for T=double it will be double4
}
You can find implementation here

SWIG and triggering a Python callback from C code

Apologies for not being familiar with formatting on here...I've made
some progress thanks to helpful replies and edited and removed my original
question to be replaced by the current one.
My problem lies with converting a C struct or struct pointer to PyObject. There
is no alternative to this because I am wrapping an existing C library whose
callback requires a C struct pointer.
Following works but with limitations:
%module cain1
%{
typedef struct {
double price;
int volume;
} book_entry_t;
typedef struct {
char symbol[10];
book_entry_t *book;
} trade_t;
typedef void (*CALLBACK)(trade_t trade);
CALLBACK my_callback = 0;
static PyObject *my_pycallback = NULL;
static void bigSnake(trade_t trade)
{
PyObject *result;
PyObject *d1;
result = PyEval_CallObject(my_pycallback,
Py_BuildValue("(y#)",
(char*)&trade,
sizeof(trade_t)
)
);
Py_XDECREF(result);
return /*void*/;
}
void test_cb (PyObject *callMe1) {
trade_t d1;
book_entry_t b1;
b1.price = 123.45;
b1.volume = 99;
Py_XINCREF(callMe1); /* Add a reference to new callback */
my_pycallback = callMe1; /* Remember new callback */
strcpy (d1.symbol,"Gupta Ltd");
d1.book = &b1;
bigSnake(d1);
}
%}
// Expose in python module..
typedef struct {
double price;
int volume;
} book_entry_t;
typedef struct {
char symbol[10];
book_entry_t *book;
} trade_t;
void test_cb(PyObject *callMe1);
and then triggering the callback from Python:
import cain1
import struct
def dave(d1):
N1,N2 = struct.unpack('10sP', d1)
print ('\n %s: %x' % (N1.decode() ,N2))
price,volume = struct.unpack('di',N2)
print (price,volume)
def main():
cain1.test_cb(dave)
main()
but I am unable to recover the book_entry_t strcut contents pointed to by trade_t....
I just feel this is all too convoluted since I have the pointer to structs and there
must be a straightforward way for Python to use that without any fuss.
Py_BuildValue("(N)",details) expects a PyObject* (your "N" says so), and you pass it something very different. Try Py_BuildValue("(i)", details.index) instead, and change it to accomodate any changes in details_t.
You're attempting to build a PyObject from a details_t struct. This isn't valid. Either pass the callback an integer (seems easier since details_t only has the one field) OR create a proper PyObject type. You can't blindly cast one type to another and expect it to work (a PyObject is more than just a pointer).

How can I collapse multiple arguments into one SWIG parameter

I'm trying to write a typemap that converts multiple/variable arguments into one input parameter.
For example, say I have a function that takes a vector.
void foo(vector<int> x);
And I want to call it like this (happens to be in Perl)
foo(1,2,3,4);
The typemap should take arguments ($argnum, ...), gather them into one vector and then pass that to foo.
I have this so far:
typedef vector<int> vectori;
%typemap(in) (vectori) {
for (int i=$argnum-1; i<items; i++) {
$1->push_back( <argv i> ); // This is language dependent, of course.
}
}
This would work, except that SWIG checks the number of arguments
if ((items < 1) || (items > 1)) {
SWIG_croak("Usage: foo(vectori);");
}
If I do:
void foo(vectori, ...);
SWIG will expect to call foo with two arguments.
foo(arg1, arg2);
Perhaps there's a way to tell SWIG to suppress arg2 from the call to foo?
I can't use this in my .i:
void foo(...)
because I want to have different typemaps, depending on the types that foo is expecting (an array of int, strings, whatever). Maybe there's a way to give a type to "..."
Is there a way to do this?
SWIG has built-in support for some STL classes. Try this for your SWIG .i file:
%module mymod
%{
#include <vector>
#include <string>
void foo_int(std::vector<int> i);
void foo_str(std::vector<std::string> i);
%}
%include <std_vector.i>
%include <std_string.i>
// Declare each template used so SWIG exports an interface.
%template(vector_int) std::vector<int>;
%template(vector_str) std::vector<std::string>;
void foo_int(std::vector<int> i);
void foo_str(std::vector<std::string> i);
Then call it with array syntax in the language of choice:
#Python
import mymod
mymod.foo_int([1,2,3,4])
mymod.foo_str(['abc','def','ghi'])
SWIG determines the argument count at the time SWIG generates the bindings. SWIG does provide some limited support for variable argument lists but I'm not sure this is the right approach to take. If you're interested, you can read more about it in the SWIG vararg documentation section.
I think a better approach would be to pass these values in as an array reference. Your typemap would then look something like this (not tested):
%typemap(in) vectori (vector<int> tmp)
{
if (!SvROK($input))
croak("Argument $argnum is not a reference.");
if (SvTYPE(SvRV($input)) != SVt_PVAV)
croak("Argument $argnum is not an array.");
$1 = &$tmp;
AV *arrayValue = (AV*)SvRV($input);
int arrayLen = av_len(arrayLen);
for (int i=0; i<=arrayLen; ++i)
{
SV* scalarValue = av_fetch(arrayValue , i, 0);
$1->push_back( SvPV(*scalarValue, PL_na) );
}
};
Then from Perl you'd use array notation:
#myarray = (1, 2, 3, 4);
foo(\#myarray);