Ctypes: Error in Returning struct pointer and accessing its elements

Viewed 222

So I have the following C struct below:

struct Matrix {
    int ncol;
    int nrow;
    double **mat;
};
typedef struct Matrix Matrix; // can do Matrix *myMat; instead of struct Matrix *myMat;

I have the following Python object to store it below:

import ctypes
library = ctypes.CDLL(r"C:myDirectory\NumLib.so")
def MATRIX(Structure):
    _fields_ = [('ncol', ctypes.c_int),
                ('nrow', ctypes.c_int),
                ('mat', ctypes.POINTER(ctypes.POINTER(ctypes.c_double)))]

I have a C function (calculator_matrix) that returns a pointer to a Matrix struct,

Matrix* calculator_matrix() {
    return parseReturn_getMatrix(ret, 1); // irrelevant function
}

to which I call in Python by:

library.calculator_matrix.argtypes = []
library.calculator_matrix.restype = MATRIX

where library is my shared library. The problem is that all my C functions work locally in C, meaning calculator_matrix() returns a Matrix* when called in a C file, but when I call the function in Python it returns None

mat = library.calculator_matrix()
print(mat) # this just prints None

This should be working just fine, as it works in a local C file by itself but when I implement it into Python it does not seem to translate the correct type. Is there something I'm doing wrong?

Side Note

So some have suggested using library.calculator_matrix.restype = POINTER(MATRIX) instead of library.calculator_matrix.restype = MATRIX , but I get the following error: TypeError: must be a ctypes type making it seem that the MATRIX object in Python isn't a ctypes type, to which it should be.

I've noticed that most ctypes tutorials included ctypes.Structure for their Python C struct container but I get the following error SyntaxError: invalid syntax when I use

def Matrix(ctypes.Structure) so that is why I used def Matrix(Structure) instead. I think that may be the problem why I get the TypeError: must be a ctypes type on the suggestion

library.calculator_matrix.restype = POINTER(MATRIX) 

Update

The issue was that there was typo of declaring MATRIX() as def instead of class, see selected answer below for a good way to declare and access elements of a ctypes struct

1 Answers

.restype is wrong and the MATRIX is is declared wrong. Here's a minimal reproducible example.

test.c

#include <stdlib.h>

typedef struct Matrix {
    int ncol;
    int nrow;
    double **mat;
} Matrix;

__declspec(dllexport)
Matrix* calculator_matrix() {
    Matrix* p = malloc(sizeof(Matrix));
    p->ncol = 2;
    p->nrow = 2;
    p->mat = malloc(2 * sizeof(double*));
    p->mat[0] = malloc(2 * sizeof(double));
    p->mat[1] = malloc(2 * sizeof(double));
    p->mat[0][0] = 1;
    p->mat[0][1] = 2;
    p->mat[1][0] = 3;
    p->mat[1][1] = 4;
    return p;
}

test.py

import ctypes

class MATRIX(ctypes.Structure): # fix here
    _fields_ = [('ncol', ctypes.c_int),
                ('nrow', ctypes.c_int),
                ('mat', ctypes.POINTER(ctypes.POINTER(ctypes.c_double)))]

library = ctypes.CDLL('./test')
library.calculator_matrix.argtypes = ()
library.calculator_matrix.restype = ctypes.POINTER(MATRIX) # fix here

mat = library.calculator_matrix().contents
print(mat.ncol,mat.nrow)
print(mat.mat[0][0],mat.mat[0][1])
print(mat.mat[1][0],mat.mat[1][1])

Output:

2 2
1.0 2.0
3.0 4.0
Related