0

I'm using SWIG to link some C libraries I wrote to Python. One of my C functions takes a struct as a parameter, and one of its data fields is a pointer (uint32_t *data). Now I need to read in a file in Python and pass the contents to that pointer. I did it like this:

p = struct_t() # create the C struct 
p.data = open(fp, 'r').read() # let the pointer point to the buffer
feria_op(p) # call the C routine

But it complains type mismatch when I run it. I'd appreciate any help, thanks in advance.

1
  • why is this tagged both ctypes and swig? The solution for each is very different and you've not actually shown a complete enough example to even be able to figure out which you're using. Commented Nov 14, 2014 at 23:39

1 Answer 1

2

As an example let's work with the following header file (lib.h):

#include <stdint.h>

struct foo {
  const uint32_t *data;
};

void bar(struct foo *f);

and a corresponding implementation (lib.c):

#include "lib.h"
#include <stdio.h>
#include <assert.h>

void bar(struct foo *f) {
  assert(f);
  assert(f->data);
  for (unsigned i = 1; i <= f->data[0]; ++i) {
    fprintf(stdout, "%d\n", (int)f->data[i]);
  }
}

I compiled this with:

gcc -Wall -Wextra -shared -o lib.so lib.c -std=c99 -g

and generated some test data in a file (input.dat) with:

with open('input.dat', 'wb') as f:
  f.write(struct.pack("IIII", 3,1,2,3))

this assumes that the data you're planing to read is already in machine endian form in your file.

Passing the contents of input.dat from Python to bar via the foo.data can be done with both ctypes and SWIG.

Ctypes

import ctypes

lib = ctypes.CDLL("lib.so")

class foo(ctypes.Structure):
  _fields_ = [('data', ctypes.POINTER(ctypes.c_uint32))]

bar = lib.bar
bar.argtypes = [ctypes.POINTER(foo)]

# Call the function with the argument:

arg = foo()

with open('input.dat', 'rb') as f:
  arg.data = ctypes.cast(ctypes.create_string_buffer(f.read()), ctypes.POINTER(ctypes.c_uint32))

bar(ctypes.byref(arg))

The main work here is the call to ctypes.cast with the string buffer created from what we read in from the file that allows the assignment to happen (correctly).

(I think the call to ctypes.byref(arg) when calling bar is redundant also because we already set argtypes for bar).

SWIG

For SWIG things are a little different and not the simplest SWIG usage. This is because of the low-level casting we need to do. (Normally you'd do %array_class and build the array in Python, or simply use a Python list instead).

This is the module interface I used for a SWIG equivalent:

%module method2

%{
#include "lib.h"
%}

%ignore foo::data; // #1

%include "lib.h"

%rename("%s") foo::data; // #2

// #3
%{
void foo_data_set(struct foo *f, char const *data) {
  f->data = data;
}
const char *foo_data_get(const struct foo *f) {
  return NULL; // Hard to make this meaningful
}
%}

%extend foo {
  const char *data; // #4
}
  1. Tell SWIG to ignore foo::data when it see it inside the headerfile - we want to replace it with something that can be used as a String
  2. After reading lib.h clear the %ignore so we can %extend and add our alternative version
  3. C code only implementation of getters and setters for our special version of foo::data. All the hardwork is actually done behind the scenes for us by he SWIG libraries.
  4. Tell SWIG that we'd like to pretend that foo::data is a const char * instead.

We build it with:

swig -python -Wall method2.i && gcc -Wall -Wextra method2_wrap.c -I/usr/include/python2.7/ -lpython2.7 -shared -o _method2.so ./lib.so

This lets us write something like:

from method2 import *

# Call the function with the argument:

arg = foo()

with open('input.dat', 'rb') as f:
  arg.data = f.read()

bar(arg)

Which is simple and works exactly as you'd hope. (Note: this only works with Python 2.7, a Python 3 solution is slightly more complex since f.read() is bytes not str)

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for the detailed answer. But here do we have to delate data as a const? Because I cannot change the data type definitions. And can you throw more light on void foo_data_set(struct foo *f, char const *data) and const char *foo_data_get(const struct foo *f)? What are they used for and is it a naming convention: struct_name_data_set? Thanks a lot.
@JFreebird It doesn't have to be const (in fact it makes no difference for the ctypes case) but it simplifies things for SWIG because then you can reuse the string code. The names are expected, by convention when you use %extend to add a member variable as opposed to a member function, because you can't add more storage to the underlying C++ type so you have to specify the behaviour explicitly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.