前回、前々回とuctypesモジュールを使ってCの「構造体」的なものにアクセスしてみました。しかしMicroPythonには、その名もズバリのstructモジュールがあるのです。こちらはCの「構造体」的なものにパックしたりアンパックしたりできるもの。「似たもの同士」だから、相互に運用可能じゃないかい?やってみました。
※「MicroPython的午睡」投稿順 Indexはこちら
前回までのuctypesモジュールは、Cのような「構造体アクセス」ができる便利なモジュールでした。しかし直接メモリ・アドレスを指定して読み書きできてしまう「ヤバい奴」でもありました。まあ、ハードウエア・レジスタの直接読み書きなどをやりたかったのでそのヤバさが魅力なのでありますが。
一方今回のstructモジュールにはそういうヤバさはありません。所望のフォーマットに従ってデータをメモリ上に詰め込み(パック)、詰め込んだデータをフォーマットに従って取り出し(アンパック)できるもの。しかし、メモリ上にパックされたデータを考えるならば、uctypesモジュールが想定している「構造体」と同じじゃないでしょか?
structモジュールについてのマニュアルページ(日本語)は以下です。
uctypesモジュールの方が、アクセスはいかにも struct風。structモジュールの方は、pack/unpackで見た目は違うと。構造体に見えるのはメモリの上の配置を想像しないとなりません。心眼?違うか?
実験に使用したMicropythonスクリプト
以下では、まずstructモジュールを使って、符号無し32ビット整数(書式指定文字 ‘I’ )を2個packしています。structが受け付ける書式指定文字はMicroPythonのマニュアルページにはなく、ご本家Pythonのマニュアルを見よ、という感じです。
https://docs.python.org/ja/3.10/library/struct.html
書式指定文字で指定した順番に値を並べていけば、メモリ上にパックしたオブジェクト(まさにCのstruct的な)が出来上がります。
structモジュール的に、ここからデータを取り出すときはunpack()メソッドを使い、取り出す(再び書式指定必要)ことになります。取り出した結果はタプルで返ります。
一方、メモリ上にpackされた状態で、uctypesモジュールによりSTRUCTをその上にマッピングしてやれば、わざわざunpack()してデータを取り出さなくてもアクセスできる筈。こんな感じ。
import uctypes import struct STRUCT1 = { "field0" : 0 | uctypes.UINT32, "field1" : 4 | uctypes.UINT32, } def main(): pk0 = struct.pack('II', 0x87654321, 0xFEDCBA98) adr0 = uctypes.addressof(pk0) tpl = struct.unpack('II', pk0) print("pk0>adr : 0x{0:08x}".format(adr0)) print("tpl0 : 0x{0:08x}".format(tpl[0])) print("tpl1 : 0x{0:08x}".format(tpl[1])) sct1 = uctypes.struct(adr0, STRUCT1) print("1>field0 : 0x{0:08x}".format(sct1.field0)) print("1>field1 : 0x{0:08x}".format(sct1.field1)) if __name__ == "__main__": main()
pack/unpack にせよ、uctypes.structにせよ、どのようなメモリ構造であるのかはプログラマ制御であるので要注意であります。しかし、分かっていれば何でもできる~っと。それが一番だな。
実験結果
Raspberry Pi Pico上のMicroPythonに上記スクリプトを送り込み、実行させたところが以下に。
unpackで取り出しても、uctypes.structで構造体を「重ねても」同じ結果が得られましたです。あたりまえか。