22 Mar 2013

C shared objects exported to python (ctypes)

Some C Projects need a quick extension which is not performance relevant. Sometimes I wish to use python because it is available on all modern *nix systems. Also, programming in python is very efficient.

To solve this I have created a small example project which puts the functionality into a shared object (dll in windows-speak) which can be used from python.

The library:

lib.c
This library contains a simple function which appends the string "->aaa" to an existing string. This is an interesting example since it involves dynamic memory allocation and pointers.


#include <stdlib.h>
#include <string.h>
#include "lib.h"

//int main() {return 0;}

char *append(char* str) {
 //char *n = "";
 char *b = "->aaa";
 char *n = malloc(strlen(str) + strlen(b) + 1);
 n[0] = '\0';
 strcat(n, str);
 strcat(n, b);
 return n;
}


lib.h
char *append(char* str);


test.c
used for testing the the library from C.

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

int main(int argc, char* argv[]) {
 printf("%s\n", append(argv[1]));
 return 0;
}

Compiling
#!/usr/bin/env bash
gcc -std=c99 -Wall -c -fPIC lib.c -o lib.o
gcc -std=c99 -Wall -shared -Wl,-soname,libt.so -o libt.so lib.o
gcc -std=c99 -Wall -o test test.c -L. -lt
rm lib.o


pylibt.py
Because python uses dynamic data types we need to give some hints about data types. Also pointers need to be declared.
#!/usr/bin/env python

from ctypes import *

cdll.LoadLibrary("./libt.so")
_libt = CDLL("libt.so")

#def initpylibt():
# pass

def append(s):
 _append = _libt.append
 _append.argtypes = [c_char_p]
 _append.restype = c_char_p
 return _append(s)


test.py
let's use the python module from a script.

#!/usr/bin/env python

import pylibt

ret = pylibt.append("0123456789as dfasdf asdf asdf asdf asf asdf")
print ret