[ctypes] Python 에서 C로 void * 로 여러 포인터를 넘길 때 Develop Tip

지난번에 char **포인터를 파이썬에서 shared c 로 넘길 때 방법을 살펴보았습니다.

이번에는 임의의 리스트를 void 포인터로 변환하여 C로 넘기는 것을 살펴보겠습니다.

우선 vp.c 라는 파일에

#include <stdio.h>

typedef enum {
    VP_NONE,
    VP_INT,
    VP_DOUBLE,
    VP_CHAR
} vptype;

int vpfunc(int type, void *vp, int nele)
{
    int i;
    int *ip = NULL;
    double *dp = NULL;
    char **cp = NULL;

    printf("C: vpfunc(%d, <%016lx>, %d)\n", type, (long unsigned int)vp, nele);
    switch(type) {
    case VP_INT:
        printf("INT array is passed...\n");
        ip = (int *)vp;
        for (i = 0; i < nele; ++i) {
            printf("\t[%d] %d\n", i, ip[i]);
        }
        break;
    case VP_DOUBLE:
        printf("DOUBLE array is passed...\n");
        dp = (double *)vp;
        for (i = 0; i < nele; ++i) {
            printf("\t[%d] %f\n", i, dp[i]);
        }
        break;
    case VP_CHAR:
        printf("CHAR array is passed...\n");
        cp = (char **)vp;
        for (i = 0; i < nele; ++i) {
            printf("\t[%d] '%s'\n", i, cp[i]);
        }
        break;
    default:
        printf("Invalid type <%d>\n", type);
    }
    return (int)type;
}

위와 같은 코드가 있습니다.

vpfunc 함수는
type: 1-INT, 2-DOUBLE, 3-char* 의 자료형이 넘어 온다는 것을 의미하고,
vp : void * 로 캐스팅된 각각의 array를 담고 있는 포인터가 넘어오고
nele: 목록의 항목 개수

이런 식으로 넘어오고 이것을 다시 원본 어레이로 캐스팅하여 화면에 출력해 보는 것입니다.

우선 shared library로 만들기 위하여,

gcc -c -fPIC vp.c
gcc -shared vp.o -o libvp.so

라고 libvp.so 를 생성하였습니다.


이제는 vp.py 라는 상단에서 호출할 파이썬 파일을 살펴보겠습니다.

#!/usr/bin/env python
#coding=utf8
import ctypes
class VP:
    #=====================================================================================
    VP_NONE = 0
    VP_INT = 1
    VP_DOUBLE = 2
    VP_CHAR = 3
    #=====================================================================================
    sofile = './libvp.so'
    clib = ctypes.CDLL(sofile)
    # int vpfunc(int type, void *vp, int nele)
    clib.vpfunc.argtypes = (ctypes.c_int, ctypes.c_void_p, ctypes.c_int)
    clib.vpfunc.restype = ctypes.c_int
    #=====================================================================================
    def call_vpfunc(self, vplist):
        if not isinstance(vplist,(list,tuple)):
            raise RuntimeError("Only list (or tuple) is needed!")
        if not vplist:
            raise ReferenceError("Empty list(or tuple) is not allowed!")
        nele = len(vplist)
        vptype = self.VP_NONE
        if isinstance(vplist[0], int):
            packed_data = (ctypes.c_int * nele)(*vplist)
            vptype = self.VP_INT
        elif isinstance(vplist[0], float):
            packed_data = (ctypes.c_double * nele)(*vplist)
            vptype = self.VP_DOUBLE
        elif isinstance(vplist[0], str):
            packed_data = (ctypes.c_char_p * nele)(*vplist)
            vptype = self.VP_CHAR
        else:
            raise RuntimeError("Invalid type")
        pv_addr = ctypes.addressof(packed_data)
        print "Python: vpfunc(%d, <%016x>, %d)" % (vptype, pv_addr,  nele)
        r = self.clib.vpfunc(vptype, pv_addr, nele)
        print r

vp = VP()
vp.call_vpfunc([1,2,3,4])
vp.call_vpfunc([1.1,2.2,3,44.4,5.5])
vp.call_vpfunc(['a','bb','ccc','가나다','우리는','국민'])

위와 같이 저장한 다음,

python vp.py
를 호출하면

Python: vpfunc(1, <00007f86e25fd6d0>, 4)
In vpfunc: type=1, vp=<00007f86e25fd6d0>, nele=4
INT array is passed...
    [0] 1
    [1] 2
    [2] 3
    [3] 4
1
Python: vpfunc(2, <0000000001ed2f90>, 5)
In vpfunc: type=2, vp=<0000000001ed2f90>, nele=5
DOUBLE array is passed...
    [0] 1.100000
    [1] 2.200000
    [2] 3.000000
    [3] 44.400000
    [4] 5.500000
2
Python: vpfunc(3, <0000000001ed3510>, 6)
In vpfunc: type=3, vp=<0000000001ed3510>, nele=6
CHAR array is passed...
    [0] 'a'
    [1] 'bb'
    [2] 'ccc'
    [3] '가나다'
    [4] '우리는'
    [5] '국민'
3

위와 같이 INT, DOUBLE, CHAR* 형식의 파이썬에서 만든 list가 shared lib로 호출되어
잘 넘어감을 확인할 수 있습니다.


어느 분께는 도움이 되셨기를...



덧글

댓글 입력 영역

구글애드텍스트