2

I have structs in a C library that are like this. The function pointers in DataFn point to static functions.

.h

struct Data {
    int i;
    int *array;
};

typedef struct {
    bool (* const fn1) (struct Data*, const char *source);
    ....
} DataFn;
extern DataFn const DATAFUNC

Using objdump, the table only contains DATAFUNC and a few other things from gcc.

This is fine in C where calling fn1 would go like DATAFUNC.fn1(..., ...), but how would something like this be wrapped around so fn1 can be called in python w/ ctypes?

Example python

libc = ctypes.cdll.LoadLibrary("./data.so")
print(libc.DATAFUNC)

results in <_FuncPtr object at 0x6ffffcd7430>

This is similar, but there isn't a factory function.

1 Answer 1

4

[Python.Docs]: ctypes - A foreign function library for Python contains everything required to solve this problem.

I believe that the main piece missing, was the in_dll method of a CTypes type (Accessing values exported from dll section).

Other than that, in order to work with C data, you need to let Python know of the data format. That applies to:

  • structs. Define Python counterparts by subclassing ctypes.Structure

  • Function pointers (applies to your case). Define them using ctypes.CFUNCTYPE

I prepared a simplified example that illustrates the above. Note that I didn't do any error handling (checking for NULLs (which you should)), to keep things simple.

dll00.h:

struct Data {
    int i;
};


typedef struct {
    int (* const Func00Ptr) (struct Data*, const char*);
} DataFunc;


extern DataFunc const dataFunc;

dll00.c:

#include <stdio.h>

#include "dll00.h"


static int func00(struct Data *pData, const char *source)
{
    printf("From C - Data.i: [%d], source: [%s]\n", pData->i, source);
    return -255;
}


DataFunc const dataFunc = { &func00 };

code00.py:

#!/usr/bin/env python

import ctypes as ct
import sys


DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")


class Data(ct.Structure):
    _fields_ = (
        ("i", ct.c_int),
    )


Func00Type = ct.CFUNCTYPE(ct.c_int, ct.POINTER(Data), ct.c_char_p)


class DataFunc(ct.Structure):
    _fields_ = (
        ("func00", Func00Type),
    )


def main(*argv):
    data = Data(127)
    dll = ct.CDLL(DLL_NAME)
    data_func = DataFunc.in_dll(dll, "dataFunc")
    ret = data_func.func00(ct.byref(data), "abcd".encode())
    print("Function returned: {:d}".format(ret))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Output:

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049962265]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> ls
dll00.c  dll00.h  code00.py
[064bit prompt]> gcc -shared -fPIC -o dll00.so dll00.c
[064bit prompt]> ls
dll00.c  dll00.h  code.py  dll00.so
[064bit prompt]> objdump -t dll00.so | grep dataFunc
0000000000200e10 g     O .data.rel.ro   0000000000000008              dataFunc
[064bit prompt]> python code00.py
Python 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609] 064bit on linux

From C - Data.i: [127], source: [abcd]
Function returned: -255

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

Comments

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.