ctypes構造体(Structure)汎用フォーマット出力印刷等


背景pythonc\c++を混合してプログラミングする場合、私たちは通常pythonctypesスキームを使用します.この場合、符号化の過程でcの構造体structと付き合うことは避けられません.符号化プロセス、特にデバッグでは、構造体情報を容易に表示またはログ印刷する必要がある場合があります.cの方法で構造体メンバー一人一人が手動で出力を符号化すると、複雑で手間がかかります.そのため、構造体オブジェクト情報を容易に調べるために、一般的な の機能を実現する必要があります.本稿では、上記の要件に基づいて実装されるスキームの1つである.ベースクラスを定義し、構造体メンバー変数のフォーマット出力を実現する.ここで、dump_dictは構造体を辞書に変換し、__str__はオブジェクトの文字列タイプ変換を実現し、showは`pprint`を使用して辞書データ結果を出力する.
実装およびコード検証
1.出力構造体情報のフォーマット
ベースクラスの定義
#         
class myStructure(Structure):
    pass

次に、クラスmyStructureでデルのニーズを実現します.dump_dictは次のとおりです.
#       
def dump_dict(self):
    info = {}
    #   _fields_       
    #          ,          
    #       
    for k, v in self._fields_:
        av = getattr(self, k)
        if type(v) == type(Structure):
            av = av.dump_dict()
        elif type(v) == type(Array):
            av = cast(av, c_char_p).value.decode()
        else:
            pass
        info[k] = av
    return info

構造体のフィールドセット_fields_を巡回し、getattrを使用して対応するメンバー変数の属性値情報を取得し、その情報に基づいて出力をフォーマットします.ここでは辞書タイプにフォーマットします.ここで、 および タイプについては特別な処理が必要であり、 は再帰的に呼び出す必要があり、 は文字列で出力される(私の実際の使用では配列は文字列であるため).
ここで、__str__およびshowは、それぞれ関数dump_dictに基づいて、必要な機能に再カプセル化すればよい.
#      
def __str__(self):
    info = self.dump_dict()
    return repr(info)

#     
def show(self):
    from pprint import pprint
    pprint(self.dump_dict())

次に、書き込みコードテストの実装効果
#     
class ST_ADDR(myStructure):
    _fields_ = [
        ('Addr', c_char*32),
        ('Port', c_int),
    ]

# ST_ADDR     
addr = ST_ADDR()
#   :{'Addr': '', 'Port': 0}
addr.show()
#   :{'Addr': '', 'Port': 0}
print(addr)

#   
addr.Addr = b'127.0.0.1'
addr.Port = 8080
#   :{'Addr': '127.0.0.1', 'Port': 8080}
addr.show()

テストの発見効果は悪くなく、出力結果は比較的友好的で、詳細はコード注釈を参照してください.
ネストされた構造体のテストコード:
#     
class ST_ADDR(myStructure):
    _fields_ = [
        ('Addr', c_char*32),
        ('Port', c_int),
    ]

# HOOK
class ST_HOOK(myStructure):
    _fields_ = [
        ('UserID', c_uint),         #     ID  
        ('HostName', c_char * 64),  #    
        ('QueueName', c_char * 64), #    
        ('QueueType', c_uint),      #     
    ]

# ST_PACKHEAD:    
class ST_PACKHEAD(myStructure):
    _fields_ = [
        ('RequestType', c_uint),                    #     (    )
        ('addr', ST_ADDR),                          #       (6   )
        ('hook', ST_HOOK),                          #         (         )
        ('userdata', c_uint),                       #        (        )
        ('ParmBits',c_ubyte*64),                    #       
    ]


def main():
    # ST_ADDR     
    addr = ST_ADDR()
    #   :{'Addr': '', 'Port': 0}
    addr.show()
    #   :{'Addr': '', 'Port': 0}
    print(addr)

    #   
    addr.Addr = b'127.0.0.1'
    addr.Port = 8080
    #   :{'Addr': '127.0.0.1', 'Port': 8080}
    addr.show()

    # ST_HOOK     
    hook = ST_HOOK()
    #   :{'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0}
    hook.show()
    #   :{'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}
    print(hook)
    #   
    hook.UserID = 578
    hook.HostName = b'127.0.0.1'
    hook.QueueName = b'q_req'
    hook.QueueType = 5205
    #   :{'UserID': 578, 'HostName': '127.0.0.1', 'QueueName': 'q_req', 'QueueType': 5205}
    print(hook)

    # ST_PACKHEAD      ,     
    head = ST_PACKHEAD()
    #       :
    # 'ParmBits': '',
    # 'RequestType': 0,
    # 'addr': {'Addr': '', 'Port': 0},
    # 'hook': {'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0},
    # 'userdata': 0}
    head.show()

    #   :{'RequestType': 0, 'addr': {'Addr': '', 'Port': 0}, 'hook': {'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}, 'userdata': 0, 'ParmBits': ''}
    print(head)

    #   
    head.RequestType = 404456
    head.userdata = 1234
    head.addr.Addr = b'127.0.0.1'
    head.hook = hook
    #       :
    # {'ParmBits': '',
    #  'RequestType': 404456,
    #  'addr': {'Addr': '127.0.0.1', 'Port': 0},
    #  'hook': {'HostName': '127.0.0.1',
    #           'QueueName': 'q_req',
    #           'QueueType': 5205,
    #           'UserID': 578},
    #  'userdata': 1234}
    head.show()

ネストされた複雑な構造体は依然として正常に出力できることが分かった、|゚ρ゚)ノ哦!work over~~~
2.メモリアドレス操作、文字列メモリブロック出力
構造体を直接メモリカードアドレスに変換し、そのデータを出力し、コードを以下のように実現する.
#      
def string(self):
    return string_at(addressof(self), sizeof(self))

テストコード:
# ST_ADDR     
addr = ST_ADDR()
addr.Addr = b'127.0.0.1'
addr.Port = 8080
print(addr.string())

出力結果:
b'127.0.0.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x1f\x00\x00'

3.メモリアドレス操作、バイナリフォーマット出力
指定したメモリブロックのデータに対して、統一的なデータデータ型で出力を解析し、一般的に純文字列またはその他の簡単なタイプの配列を出力するために使用され、実装コードは以下の通りである.
#           
#    ctp :         ,    ctypes    , c_char, c_int
#    row_num :       
#    full :         ,           ctp      ,  ctp    c_char  
def show_meminfo(self, ctp=c_char, row_num = 8, full = False):
    import struct
    # #      , char  
    if full and sizeof(self)%sizeof(ctp):
        ctp = c_char
    block = int(sizeof(self)/sizeof(ctp))
    addr = string_at(addressof(self), sizeof(self))
    for i in range(block):
        v = struct.unpack(ctp._type_, addr[i*sizeof(ctp):(i+1)*sizeof(ctp)])
        pend =  '
'
if (i+1)%row_num==0 else ' ' print(repr(v[0]), end=pend) print()

テストコードは次のとおりです.
class ST_DATA(myStructure):
    _fields_ = [
        ('day1', c_int*3),
        ('day2', c_int * 4),
        ('day3', c_int * 5),
        ('day4', c_int * 6),
        ('day5', c_int * 7),
    ]

def main():
    # ST_DATA     
    data = ST_DATA()
    data.day1 = (c_int*3)(11, 12, 13)
    data.day2 = (c_int * 4)(21, 22, 23, 24)
    data.day3 = (c_int * 5)(31, 32, 33, 34, 35)
    data.day4 = (c_int * 6)(41, 42, 43, 44, 45, 46)
    data.day5 = (c_int * 7)(51, 52, 53, 54, 55, 56, 57)
    data.show_meminfo(c_int, 4)

出力結果は次のとおりです.
11 12 13 21
22 23 24 31
32 33 34 35
41 42 43 44
45 46 51 52
53 54 55 56
57 

出力結果のデータはc_intユニットで解析され、出力が整列していることがわかります.
完全なコードとテスト例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from ctypes import *

#         
class myStructure(Structure):
    #       
    def dump_dict(self):
        info = {}
        #   _fields_       
        #          ,          
        #       
        for k, v in self._fields_:
            av = getattr(self, k)
            if type(v) == type(Structure):
                av = av.dump_dict()
            elif type(v) == type(Array):
                av = cast(av, c_char_p).value.decode()
            else:
                pass
            info[k] = av
        return info

    #      
    def __str__(self):
        info = self.dump_dict()
        return repr(info)

    #     
    def show(self):
        from pprint import pprint
        pprint(self.dump_dict())

#     
class ST_ADDR(myStructure):
    _fields_ = [
        ('Addr', c_char*32),
        ('Port', c_int),
    ]

# HOOK
class ST_HOOK(myStructure):
    _fields_ = [
        ('UserID', c_uint),         #     ID  
        ('HostName', c_char * 64),  #    
        ('QueueName', c_char * 64), #    
        ('QueueType', c_uint),      #     
    ]

# ST_PACKHEAD:    
class ST_PACKHEAD(myStructure):
    _fields_ = [
        ('RequestType', c_uint),                    #     (    )
        ('addr', ST_ADDR),                          #       (6   )
        ('hook', ST_HOOK),                          #         (         )
        ('userdata', c_uint),                       #        (        )
        ('ParmBits',c_ubyte*64),                    #       
    ]


def main():
    # ST_ADDR     
    addr = ST_ADDR()
    #   :{'Addr': '', 'Port': 0}
    addr.show()
    #   :{'Addr': '', 'Port': 0}
    print(addr)

    #   
    addr.Addr = b'127.0.0.1'
    addr.Port = 8080
    #   :{'Addr': '127.0.0.1', 'Port': 8080}
    addr.show()

    # ST_HOOK     
    hook = ST_HOOK()
    #   :{'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0}
    hook.show()
    #   :{'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}
    print(hook)
    #   
    hook.UserID = 578
    hook.HostName = b'127.0.0.1'
    hook.QueueName = b'q_req'
    hook.QueueType = 5205
    #   :{'UserID': 578, 'HostName': '127.0.0.1', 'QueueName': 'q_req', 'QueueType': 5205}
    print(hook)

    # ST_PACKHEAD      ,     
    head = ST_PACKHEAD()
    #       :
    # 'ParmBits': '',
    # 'RequestType': 0,
    # 'addr': {'Addr': '', 'Port': 0},
    # 'hook': {'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0},
    # 'userdata': 0}
    head.show()

    #   :{'RequestType': 0, 'addr': {'Addr': '', 'Port': 0}, 'hook': {'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}, 'userdata': 0, 'ParmBits': ''}
    print(head)

    #   
    head.RequestType = 404456
    head.userdata = 1234
    head.addr.Addr = b'127.0.0.1'
    head.hook = hook
    #       :
    # {'ParmBits': '',
    #  'RequestType': 404456,
    #  'addr': {'Addr': '127.0.0.1', 'Port': 0},
    #  'hook': {'HostName': '127.0.0.1',
    #           'QueueName': 'q_req',
    #           'QueueType': 5205,
    #           'UserID': 578},
    #  'userdata': 1234}
    head.show()


if __name__ == '__main__':
    main()