4

Recently I'm doing a project which a function is heavily called, so I want to using C code in this part. I'm a newbie to ctypes, please forgive me if my question is very easy.

Here I have a 2d list in python:

L = [[1],[1,2],[1,2,3]]

I want call a function in C module with it as parameter. Since there is no 2d-list in C I want to convert it to an array of *int.

I don't want an normal 2D C array because each length of entry is different.

What I've done in python part is:

L = [[1],[1,2],[1,2,3]]
entrylist = []
for entry in L:
  c_entry = (ctypes.c_int * len(entry))(*entry) # c_entry is the C array version of entry
  entrylist.append(c_entry)

c_L = (ctypes.POINTER(ctypes.c_int) * len(entrylist))(*entrylist) # create an array of integer pointer, then initial it

c_L is an "LP_c_long_Array_14 object", when len(L) == 14.

also, I can print it out perfectly by using

for i in range(len(L)):
  for j in range(len(L[i])):
    print(L[i][j], end = ' ')
  print()

On the other hand, in C code, I define my function as:

int fun(int** c_L)
int fun( (int * c_L)[])

neither works. ctypes throws a "Don't know how to convert parameter 1" Error.

So, please tell me how to make it work? Thank you very much.

2 Answers 2

4

You are almost there, you just need some tweaks.

import ctypes as C

lib = C.CDLL("libfoo.so")

l = [[1],[1,2],[1,2,3]]
entrylist = []
lengths = []

for sub_l in l:
    entrylist.append((C.c_int*len(sub_l))(*sub_l))
    lengths.append(C.c_int(len(sub_l)))

c_l = (C.POINTER(C.c_int) * len(entrylist))(*entrylist)
c_lengths = ( C.c_int * len(l))(*lengths)

lib.test(c_l, c_lengths, len(l))  #here we also pass the sizes of all the arrays

where the C side of things looks like:

#include <stdlib.h>
#include <stdio.h>

int test(int **ar,int *lens,int n_ar){
    int ii,jj,kk;
    for (ii=0;ii<n_ar;ii++){
        for (jj=0;jj<lens[ii];jj++){
            printf("%d\t",ar[ii][jj]);
        }  
        printf("\n");
        fflush(stdout);
    }
    return 0;
}

If this doesn't work, then it may be of use to explicitly cast all your arguments to the c library function.

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

1 Comment

Amazing solution. Thank you so much.
0

I think you can avoid int** pointer by packing all you data into one list:

[1, 1, 2, 1, 2, 3]

and pass another list that represents the start of every sub list such as:

[0, 1, 3, 6]

so the first sub list starts at 0, ends at 1, the second sub list starts at 1, ends at 3, and the third sub list starts at 3 ends at 6.

convert this two list to int array and pass it to the c function with the length of the second array.

Here is an example:

#include <stdio.h>
void sum_list(int * data, int * positions, int length)
{
    int i, j, s;
    for(i=0;i<length-1;i++)
    {
        s = 0;
        for(j=positions[i];j<positions[i+1];j++)
        {
            s += data[j];
        }
        printf("%d\n", s);
    }
}

and the python code:

L = [[1],[1,2],[1,2,3]]

data = []
positions = [0]
for sub in L:
    data.extend(sub)
    positions.append(len(data))

from ctypes import *

lib = CDLL("./sum_list.out")

c_data = (c_int * len(data))(*data)
c_positions = (c_int * len(positions))(*positions)

lib.sum_list(c_data, c_positions, len(c_positions))

1 Comment

Actually thank you for inspiring answer. I used to write my c code with a len(L) times malloc in a for loop (excuse me for my amateur c level), after I mod it to an 1d array with an pointer array pointing to offset, the speed actually increased dramatically, which give me a lecture not to use malloc in loop :-)

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.