본문 바로가기

개발/Python

Python ctypes 구조체 사용

Python 의 ctypes 에는 C 언어와 유사한 구조체 기능을 제공한다.

(https://docs.python.org/2/library/ctypes.html#structured-data-types)

 

사용방법은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
import ctypes
 
 
class my_struct(ctypes.Structure):
    _fields_ = [
        ('data_1', ctypes.c_int),
        ('data_2', ctypes.c_int8),
        ('data_3', ctypes.c_uint8),
    ]
 
 
print(ctypes.sizeof(my_struct))
8


구조체 크기가 alignment 되는 것도 C 언어와 같다.

C 에서는 #pragma pack(1) 또는 __attribute__((packed)) 통해 padding 을 막듯이 여기서도 비슷하게 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ctypes
 
 
class my_struct(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('data_1', ctypes.c_int),
        ('data_2', ctypes.c_int8),
        ('data_3', ctypes.c_uint8),
    ]
 
 
print(ctypes.sizeof(my_struct))
6

 

bit field 설정도 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ctypes
 
 
class my_struct(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('data_1', ctypes.c_int, 8),
        ('data_2', ctypes.c_int, 8),
        ('data_3', ctypes.c_int, 16),
    ]
 
 
print(ctypes.sizeof(my_struct))
4

 

이렇게 타입을 섞으면 안된다...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ctypes
 
 
class my_struct(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('data_1', ctypes.c_int, 8),
        ('data_2', ctypes.c_int8, 8),
        ('data_3', ctypes.c_int, 16),
    ]
 
 
print(ctypes.sizeof(my_struct))
9

 

섞어도 문제없는 구조체가 필요했기에 삽질을 하며 아래와 같이 완성, 추가로 파일 read, write 기능도 넣었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import ctypes
import numpy as np
from io import BytesIO
 
 
class struct_type:
    def __init__(self, bytes, ctype, count):
        self.bytes = bytes
        self.ctype = ctype
        self.count = count
 
 
def create_struct(type_map):
    def return_fields():
        fields = list()
        for name, types in type_map.items():
            fields.append((name, ctypes.c_byte * types.bytes))
        return fields
 
    class Structure(ctypes.Structure):
        _pack_ = 1
        _fields_ = return_fields()
 
        def __setattr__(self, key, value):
            buffer_ctypes = np.ctypeslib.as_ctypes(np.zeros(type_map[key].bytes, dtype=ctypes.c_byte))
 
            if type_map[key].count == 1:
                value_ctypes = np.ctypeslib.as_ctypes(np.dtype(type_map[key].ctype).type(value))
                fit = min(type_map[key].bytes, ctypes.sizeof(value_ctypes))
                ctypes.memmove(ctypes.addressof(buffer_ctypes), ctypes.addressof(value_ctypes), fit)
 
                super(Structure, self).__setattr__(key, buffer_ctypes)
            else:
                if type_map[key].ctype is ctypes.c_char:
                    if value == 0:
                        byte = str.encode('')
                    else:
                        byte = str.encode(str(value))
 
                    value_ctypes = (ctypes.c_byte * len(byte)).from_buffer_copy(byte)
                else:
                    value_ctypes = np.ctypeslib.as_ctypes(np.asarray(value, dtype=type_map[key].ctype))
                fit = min(type_map[key].bytes, ctypes.sizeof(value_ctypes))
                ctypes.memmove(ctypes.addressof(buffer_ctypes), ctypes.addressof(value_ctypes), fit)
 
                super(Structure, self).__setattr__(key, buffer_ctypes)
 
        def __getattribute__(self, key):
            if key not in type_map:
                return super(Structure, self).__getattribute__(key)
 
            data = super(Structure, self).__getattribute__(key)
            fit = min(type_map[key].bytes, ctypes.sizeof(type_map[key].ctype) * type_map[key].count)
 
            if type_map[key].ctype is ctypes.c_char:
                return bytes(data)[0:fit].decode().strip('\x00')
            else:
                buffer_ctypes = np.ctypeslib.as_ctypes(np.zeros(type_map[key].count, dtype=type_map[key].ctype))
            ctypes.memmove(ctypes.addressof(buffer_ctypes), ctypes.addressof(data), fit)
            value = np.ctypeslib.as_array(buffer_ctypes)
 
            if type_map[key].count == 1:
                return np.asscalar(value)
            else:
                return value
 
        def clear(self):
            for name, types in type_map.items():
                setattr(self, name, 0)
 
        def write(self, filename):
            with open(filename, 'wb') as f:
                f.write(self)
            f.close()
 
        def read(self, filename):
            with open(filename, 'rb') as f:
                BytesIO(f.read()).readinto(self)
            f.close()
 
    return Structure()
 
 
my_struct_type = {
    # name, byte size, type, type count
    'data_1': struct_type(1, ctypes.c_int, 1),
    'data_2': struct_type(1, ctypes.c_int8, 1),
    'data_3': struct_type(2, ctypes.c_int, 1),
}
 
st = create_struct(my_struct_type)
 
print(ctypes.sizeof(st))
4

 

'개발 > Python' 카테고리의 다른 글

python netCDF4 패키지 설치  (0) 2018.10.04
julian date 계산  (0) 2018.10.04