8

I am trying to implement this C structures in python using ctypes:

struct _rows {
    int cols_count;
    char *cols[];
}

struct _unit {
    int rows_count;
    struct _rows *rows;
}

int my_func(struct _unit *param);

Problem is that _rows.cols is a dynamically sized array of char pointer and _unit.rows is a dynamically sized array of _rows structure. How can I implement this using ctypes in python?

I was able to define a function that will return a _rows structure with variable number of char pointers:

def get_row(cols):
    class Row(ctypes.Structure):
        _fields_ = [("cols_count", ctypes.c_int), 
                    ("cols", ctypes.c_char_p * cols)
                   ]

I don't know what to do nex, it's all a bit fuzzy and ctypes documentation isn't helping.

1
  • You are on the right track. However, it would help if you provided more information about what you are trying to accomplish. For instance, you could add some pseudo code describing what you need to do. Commented Dec 6, 2011 at 2:23

1 Answer 1

16

I'm making some assumptions about what the OP wants, and I'd love suggestions if there is an easier way to do this, but this is what I came up with:

demo.py

import string
from ctypes import Structure,c_int,c_char_p,POINTER,cast,pointer,byref,CDLL

class Row(Structure):
    _fields_ = [('cols_count', c_int), 
                ('cols', POINTER(c_char_p))]
    def __init__(self,cols):
        self.cols_count = cols
        # Allocate an array of character pointers
        pc = (c_char_p * cols)()
        self.cols = cast(pc,POINTER(c_char_p))            

class Unit(Structure):
    _fields_ = [('rows_count', c_int),
                ('rows',POINTER(Row))]
    def __init__(self,rows,cols):
        self.rows_count = rows
        # Allocate an array of Row structures.
        # This does NOT call __init__.
        pr = (Row * rows)()
        # Call init manually with the column size.
        for r in pr:
            r.__init__(cols)
        self.rows = cast(pr,POINTER(Row))

unit = Unit(2,3)

# Stuff some strings ('aaaaa','bbbbb',etc.)
for i in xrange(unit.rows_count):
    for j in xrange(unit.rows[i].cols_count):
        unit.rows[i].cols[j] = string.ascii_lowercase[i*5+j]*5

dll = CDLL('test.dll')
dll.my_func(byref(unit))

test.c

#include <stdio.h>

struct _rows {
    int cols_count;
    char **cols;
};

struct _unit {
    int rows_count;
    struct _rows *rows;
};

__declspec(dllexport) int my_func(struct _unit *param)
{
    int i,j;
    for(i=0;i<param->rows_count;i++)
        for(j=0;j<param->rows[i].cols_count;j++)
            printf("%d,%d = %s\n",i,j,param->rows[i].cols[j]);
    return 0;
}

makefile

Compiled with Visual Studio 2010.

test.dll: test.c
    cl /W4 /LD test.c

Output

0,0 = aaaaa
0,1 = bbbbb
0,2 = ccccc
1,0 = fffff
1,1 = ggggg
1,2 = hhhhh
Sign up to request clarification or add additional context in comments.

3 Comments

Excellent answer. It works exactly the way I want it. I've learned a couple of key tricks now. Thank you.
this isn't working for me, i'm trying to define a struct that ends with char[], but using POINTER(c_ubyte) results in a pointer in the structure, not an actual array of characters...
@Michael Don't use POINTER for an array. c_char * size is used for arrays. You will need a class factory to create the size needed when it is a placeholder empty array.

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.