Skip to content

Instantly share code, notes, and snippets.

@jakelevi1996
Last active April 8, 2023 17:00
Show Gist options
  • Select an option

  • Save jakelevi1996/599e647b5d63e41ca3dfaa5ff1e2405e to your computer and use it in GitHub Desktop.

Select an option

Save jakelevi1996/599e647b5d63e41ca3dfaa5ff1e2405e to your computer and use it in GitHub Desktop.
Calling a C function from Python

Calling a C function from Python

The ctypes module in Python can be used to call a function which has been written and compiled using C. This Gist demonstrates a simple example of a C-function which takes an argument of type in_struct and returns a value of type out_struct (both of these types are defined in the source code), which is compiled into a DLL and called from a Python script.

There are a few quirks to bear in mind when using ctypes:

  • From the section "Return types" of the ctypes documentation: "By default functions are assumed to return the C int type. Other return types can be specified by setting the restype attribute of the function object"
  • If using 64-bit Python, then only 64-bit DLLs can be loaded using ctypes (and similarly for 32-bit Python and 32-bit DLLs); one way to compile 64-bit DLLs in Windows is to use the x86_64-w64-mingw32-gcc C compiler, which can be installed via Cygwin
  • Functions compiled from C++ source code can also be called from Python, but they must be wrapped in an extern "C" block, as described in this Stack Overflow answer; the reason for using extern "C" is to do with the name-mangling that C++ performs during compilation in order to allow function overloading, as described in this Stack Overflow answer

Below is the C source code, and the Python script which calls the resulting binary DLL.

lib.c

/* Compile on 64-bit Windows using:
x86_64-w64-mingw32-gcc -fpic -shared -O3 lib.c -o lib64.dll
*/

typedef struct {
    int a;
    int b;
} in_struct;

typedef struct {
    int add;
    int subtract;
    int multiply;
    int max;
} out_struct;

out_struct calc_answer(in_struct i_s) {
    out_struct o_s;
    o_s.add = i_s.a + i_s.b;
    o_s.subtract = i_s.a - i_s.b;
    o_s.multiply = i_s.a * i_s.b;
    o_s.max = i_s.a > i_s.b ? i_s.a : i_s.b;
    return o_s;
}

main.py

Before running this script, make sure lib.c is present in the same directory, and lib64.dll has been compiled using the command x86_64-w64-mingw32-gcc -fpic -shared -O3 lib.c -o lib64.dll.

import os
import ctypes as ct

# Define structure that will be passed to the DLL function
class InStruct(ct.Structure):
    _fields_ = [("a", ct.c_int), ("b", ct.c_int)]

# Define structure that will be returned from the DLL function
class OutStruct(ct.Structure):
    _fields_ = [("add", ct.c_int), ("subtract", ct.c_int),
        ("multiply", ct.c_int), ("max", ct.c_int)]

# Load the DLL
lib = ct.cdll.LoadLibrary("lib64.dll")

# Get the function from the DLL and set the return type
calc_answer = lib.calc_answer
calc_answer.restype = OutStruct

def print_lib_func_values(a, b):
    i_s = InStruct(a, b)
    o_s = calc_answer(i_s)

    print("a = {}, b = {}".format(a, b))
    print("\tadd = {}, subtract = {}, multiply = {}, max = {}".format(
        o_s.add, o_s.subtract, o_s.multiply, o_s.max))

if __name__ == "__main__":
    # Test some random inputs
    print_lib_func_values(3, 4)
    print_lib_func_values(8, 5)

Output:

a = 3, b = 4
        add = 7, subtract = -1, multiply = 12, max = 4
a = 8, b = 5
        add = 13, subtract = 3, multiply = 40, max = 8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment