ポインタ

PyFIE では、 各 PyFIE データ型に対して C 言語におけるポインタに相当するものを扱うことができます。 これを PyFIE ポインタ型と呼ぶことにします。

PyFIE ポインタ型は、 各 PyFIE データ型のインスタンスが内部で保持している(C 言語互換の)メモリブロックへの参照を保持します。 ( 以下の説明では明示する場合を除き PyFIE ポインタ型インスタンスのことを、 単に "ポインタ" と表記します )



PyFIE ポインタ型の使用方法

PyFIE データ型へのポインタ

各 PyFIE データ型のインスタンスは属性 ref によりそのポインタを取得することができます。 ここで取得されるポインタはメモリアドレスを表す単なる数値ではなく PyFIE ポインタ型のインスタンスとなります。

C 言語
INT i;
&i;
Python
i = pyfie.INT()
i.ref

このように取得された PyFIE ポインタ型のインスタンスは、 属性 deref をもっています。 この属性 deref は、 そのポインタが参照している PyFIE データ型インスタンスとなります。 (つまり C 言語の逆参照に相当します)

属性 deref へ代入を行うことにより、 ポインタの参照先インスタンスへの代入が行えます。

注釈

厳密にはポインタの属性 deref への代入は、 ポインタの参照先インスタンスの属性 value への代入 (つまりインスタンスが保持しているデータ格納先への代入)となります。

下記は PyFIE 算術型に対するポインタアクセスの例です。

C 言語
INT i = 3;
INT * p = &i;

*p = 10;
Python
i = pyfie.INT(3)
p = i.ref

p.deref = 10
# ポインタ p が参照しているインスタンス
# すなわち i の値に 10 を設定.

同様に下記は PyFIE 構造体に対するポインタアクセスの例です。

C 言語
PNT_T pnt1 = { 1, 2 };
PNT_T pnt1 = { 3, 4 };

PNT_T * p1 = &pnt1;

*p1 = pnt2;

p1->x = 30;
p1->y = 40;
Python
pnt1 = pyfie.PNT_T(1, 2)
pnt2 = pyfie.PNT_T(3, 4)

p1 = pnt1.ref

p1.deref = pnt2
# 構造体のポインタを介した一括代入.
# pnt1 のメンバ値は pnt2 のメンバ値と同じになる.

p1.deref.x = 30
p1.deref.y = 40
# pnt1 のメンバに個別に値を設定する例.

注釈

ポインタの属性 deref に対する代入では、 参照先の PyFIE データ型へ変換可能なオブジェクトを使用することができます。

詳しくは各 PyFIE データ型に対する "オブジェクト変換" の説明を参照ください。


このようにポインタの属性 deref は (参照先インスタンスの複製ではなく)参照先インスタンスそのもの(厳密にはデータ格納先を共有する PyFIE データ型のインスタンス) となります。

そのため deref を変数に代入した場合には、 C 言語と直感的な挙動に違いがありますので注意してください。

下記サンプルコードは変数 j について異なる挙動を示します。

C 言語
INT i = 3;
INT * p = &i;
INT j = *p;

j = 30;
// j の値に 30 を代入しても当然 i の値は変わらない.
Python
i = pyfie.INT(3)
p = i.ref
j = p.deref

j.value = 30
# j は i と同じものなので j の値を 30 とすることにより
# i の値も 30 となる.

同様に PyFIE 構造体のポインタに関する下記サンプルコードにおいても、 変数 X および Y について異なる挙動を示します。

C 言語
PNT_T pnt = { 3, 4 };
PNT_T * p = &pnt;

INT X = p->x;
INT Y = p->y;

X = 30;
Y = 40;
// X と Y の値を変更しても
// 当然 pnt のメンバの値はそのままである.
Python
pnt = pyfie.PNT_T(3, 4)
p = pnt.ref

X = p.deref.x
Y = p.deref.y
# X は pnt.x そのもの
# Y は pnt.y そのものとなる.

X.value = 30
Y.value = 40
# X 及び Y の値を変更することにより
# pnt のメンバ(すなわち pnt.x と pnt.y)の値も変更される.

注釈

この挙動は C 言語と Python の言語仕様の違いによるものです。 詳しくは PyFIE データ型を変数に代入する際の C 言語との挙動の違い を参照して下さい。

上記 C 言語サンプルコードと同じことを行う場合は、 代入される側の PyFIE データ型インスタンスを予め用意し、 属性 value を使用します。

C 言語
INT i = 3;
INT * p = &i;
INT j;

j = *p;

j = 30;
Python
i = pyfie.INT(3)
p = i.ref
j = pyfie.INT()
# i とは別のインスタンス j を用意.

j.value = p.deref
# j の値に p の参照先(i)の値を設定.

j.value = 30
# j は i とは別のインスタンスなので、
# 値に 30 を代入しても i の値は変わらない.

ポインタ型変数

各 PyFIE データ型クラスは属性 PTR をもっています。 この属性 PTR は、そのデータ型に対する PyFIE ポインタ型クラスを表します。 (このクラスは属性 ref により得られるポインタのクラスと同じものとなります)

参照先をもたない PyFIE ポインタ型インスタンスを作成する場合は これをコンストラクタとして使用します。 これは C 言語におけるポインタ型変数の宣言に相当します。

C 言語
INT * p = NULL;
INT ** pp = NULL;
Python
p  = pyfie.INT.PTR()
pp = pyfie.INT.PTR.PTR()

このように作成された "参照先をもたないポインタ" は、 C 言語における "NULL が代入されたポインタ変数" に相当します。

注釈

参照先をもたないポインタに対し、 属性 ref や 後述する添字によるアクセスを行うと NULL ポインタアクセスとなり例外が発生します。

PyFIE ポインタ型インスタンスの参照先を別の参照先に変更する場合には属性 value を使用します。

C 言語
INT i1 = 12;
INT i2 = 34;
INT * p = NULL;

p = &i1;
*p = -12;

p = &i2;
*p = -34;
Python
i1 = pyfie.INT(12)
i2 = pyfie.INT(34)
p = pyfie.INT.PTR()

p.value = i1.ref
p.deref = -12
# i1 の値に -12 を設定.

p.value = i2.ref
p.deref = -34
# i2 の値に -12 を設定.

属性 value に 0 や None 、 または参照先をもたない他のポインタを代入した場合、 そのポインタ型インスタンスは "NULL ポインタ" となります。

C 言語
INT i = 123;
INT * p = &i;

p = NULL;
Python
i = pyfie.INT(123)
p = i.ref
# p は i を参照するポインタ型インスタンス.

p.value = None
# p は NULL ポインタとなった.

また、 異なる PyFIE データ型に対するポインタの代入は行うことができません。 つまり下記サンプルコードではエラー(例外)が発生します。

C 言語
INT i = 12;
UINT * p = NULL;

p = &i;
Python
i = pyfie.INT(12)
p = pyfie.UINT.PTR()

p.value = i.ref
# UINT 型ポインタ変数に INT 型ポインタを代入しようとした ...
# !!! Runtime Error となる !!!

このように異なる PyFIE データ型に対するポインタの代入は、 PyFIE ポインタ型クラスのクラスメソッド cast() を使用すると可能となります。

C 言語
INT i = 12;
UINT * p = NULL;

p = (UINT *)&i;
Python
i = pyfie.INT(12)
p = pyfie.UINT.PTR()

p.value = pyfie.UINT.PTR.cast(i.ref)
# INT 型ポインタを UINT 型ポインタにキャスト

注釈

ポインタの属性 value を読み出した場合、 ポインタ値(アドレス値)が得られます。


VOID.PTR

参照先として特定の PyFIE データ型を規定しない PyFIE ポインタ型として VOID.PTR 型が用意されています。 VOID.PTR 型は C 言語における "void ポインタ" に相当します。

VOID.PTR 型インスタンスの値(属性 value)には、 cast() メソッドを使用せず任意のポインタを代入することができます。

C 言語
INT i = 12;
VOID * p = NULL;

p = &i;
Python
i = pyfie.INT(12)
p = pyfie.VOID.PTR()

p.value = i.ref

0 や None の代入を行った場合、 VOID.PTR 型インスタンスは "NULL ポインタ" となります。

C 言語
INT i = 12;
VOID * p = NULL;

p = &i;
p = NULL;
Python
i = pyfie.INT(12)
p = pyfie.VOID.PTR()

p.value = i.ref
p.value = None

注釈

この他、 VOID.PTR インスタンスの属性 value には 任意のアドレス値を数値として与えることができてしまいます。 これは任意のメモリ領域へのアクセスを可能とするため、 行う場合は C 言語の場合と同様、 不正なメモリアクセス等に注意する必要があります。


また VOID.PTR 型ポインタを、 cast() メソッドを使用せず任意のポインタへ代入することができます。

C 言語
INT i = 12;
VOID * vp = NULL;
INT  * ip = NULL;

vp = &i;

ip = (INT *)vp;

*ip = 120;
Python
i = pyfie.INT(12)
vp = pyfie.VOID.PTR()
ip = pyfie.VOID.PTR()

vp.value = i.ref
# vp は i を参照するようになるが
# VOID.PTR 型であるため参照先 i にアクセスすることができない.

ip.value = vp
# vp を INT 型ポインタにキャストし ...

ip.deref = 120
# 参照先である i にアクセス.

ポインタの配列

PyFIE ポインタ型に対する PyFIE 配列は VOID.PTR 型に対してのみ作成することができます。 VOID.PTR 型の PyFIE 配列には要素として任意の PyFIE データ型ポインタを格納することができます。 したがってポインタの配列が必要な場合には、 VOID.PTR 型の PyFIE 配列を使用することになります。

VOID.PTR 型の PyFIE 配列は他の PyFIE データ型と同様、 属性 ARRAY を使用して作成します。

C 言語
UCHAR e0;
UCHAR e1;
UCHAR e2;

VOID * a[3];

a[0] = &e0;
a[1] = &e1;
a[2] = &e2;

*((UCHAR *)a[0]) = 123;
Python
e0 = pyfie.UCHAR()
e1 = pyfie.UCHAR()
e2 = pyfie.UCHAR()

a = pyfie.VOID.PTR.ARRAY(3)

a[0] = e0.ref
a[1] = e1.ref
a[2] = e2.ref

pyfie.UCHAR.PTR.cast(a[0]).deref = 123
# e0 の値に 123 を設定.

ポインタの添字アクセス

VOID.PTR 型以外のポインタでは、 配列のように添字を使った参照先インスタンスへのアクセスが可能です。

C 言語
INT i = 3;

INT * p = &i;

p[0] = 10;
Python
i = pyfie.INT(3)

p = i.ref
# p は INT.PTR 型インスタンスとなる.

p[0] = 10
# i の値に 10 を設定.

このようなポインタの添字アクセスでは、 メモリ範囲外へのアクセスが可能となってしまいます。 メモリ範囲外へのアクセスを行った場合、 例外で捕捉されず不正なメモリアクセスが発生しますので注意してください。

下記はこのような不正なメモリアクセスが発生する例です。

C 言語
INT a[5];
INT * p;

p = a;

p[4] = 10;

p[1000] = 10;
// !!! 不正なメモリアクセス !!!
Python
a = pyfie.INT.ARRAY(5)

p = a.ref
# p は配列 a へのポインタ(INT.PTR 型)となる.

p[4] = 10
# これは配列の範囲内なので問題なし.

p[10000] = 10
# !!! 不正なメモリアクセス !!!

また、 ポインタの添字アクセスにより得られるインスタンスは 先述した属性 deref の場合と同様、 参照先インスタンスそのものとなります。

これは C 言語との直感的な挙動の違いであり、 下記サンプルコードは変数 i について異なる挙動を示します。

C 言語
INT a[5];
INT * p;
INT i;

p = a;

i = p[4];

i = 10;
// i の値に 10 を代入しても
// 配列 a の要素には影響しない.
Python
a = pyfie.INT.ARRAY(5)

p = a.ref

i = p[4]

i.value = 10
# i の値を 10 とすることにより
# 配列の要素(a[4])の値が 10 となる

CHAR 型ポインタのバイト列の取得

CHAR.PTR 型インスタンスは属性 value_as_bytes をもち、 そのインスタンスが表すゼロ終端文字列を Python バイト列として取得することができます。

C 言語
CHAR st[5];
CHAR * p = st;

p[0] = 'A';
p[1] = 'B';
p[2] = 'C';
p[3] = 0;

puts(p);
// "ABC"と表示される.
Python
st = pyfie.CHAR.ARRAY(5)
p = pyfie.CHAR.PTR(st)

p[0] = ord("A")
p[1] = ord("B")
p[2] = ord("C")
p[3] = 0

print(p.value_as_bytes)
// "b'ABC'"と表示される.


PyFIE ポインタ型のオブジェクト変換

PyFIE ポインタ型は特定の状況において、 下記オブジェクトからの変換が可能となります。

  • (参照先となる) PyFIE データ型インスタンス

  • PyFIE 配列

  • 参照先となる PyFIE 配列

  • 参照先要素型のデータサイズが同じ PyFIE ポインタ型

"(参照先となる) PyFIE データ型インスタンス" や "PyFIE 配列" からの変換では、 変換元の属性 ref により得られるポインタ型が変換結果となります (つまり変換元への参照となります)。

"参照先となる PyFIE 配列" からの変換では、 変換元の属性 ref により得られるポインタ型に対し、 さらに属性 ref により得られるポインタ型が変換結果となります。

また "参照先要素型のデータサイズが同じ PyFIE ポインタ型" からの変換では、 単純にキャストされたものが変換結果となります。


さらにポインタの参照先が算術型である場合には、 下記オブジェクトからの変換が可能となります。

リスト型/タプル型から PyFIE ポインタ型への変換では、 必要なサイズの C 言語互換メモリブロックの確保が行われます (このメモリブロックは参照されなくなった時点で自動的に解放されます)。 確保されたメモリブロックは、 リスト型/タプル型から要素値のコピーが行われた後、 そのトップアドレスが変換結果として返されます (つまり、 リスト型/タプル型 への参照となるわけではありません)。


さらにポインタが VOID.PTR 型である場合には、 下記オブジェクトからの変換が可能となります。

  • Python C API の PyCapsule


これらの変換は、 下記状況において行うことができます。

  • PyFIE 構造体のポインタ型メンバに対する代入時

  • PyFIE 関数のポインタ型引数に対する指定時

以下に例を示します。

C 言語
F_FILTER_KERNEL_T kernel;
DOUBLE * p = NULL;

kernel.size_x = 3;
kernel.size_y = 3;
kernel.anchor_x = 1;
kernel.anchor_y = 1;
kernel.denom = 1;

p = (DOUBLE *)fnOAL_malloc(sizeof(DOUBLE), 3 * 3);
p[0] =  1;
p[1] =  2;
p[2] =  3;
p[3] = -1;
p[4] =  0;
p[5] =  1;
p[6] = -3;
p[7] = -2;
p[8] = -1;

fnOAL_free(kernel);
Python
kernel = pyfie.F_FILTER_KERNEL_T()

kernel.size_x = 3
kernel.size_y = 3
kernel.anchor_x = 1
kernel.anchor_y = 1
kernel.denom = 1

# 構造体のポインタ型メンバに対して
# タプル型で設定を行う例.
kernel.p = (
     1,  2,  3,
    -1,  0,  1,
    -3, -2, -1
)


PyFIE ポインタ型のメソッド

classmethod PTR.cast(ptr_obj)

このメソッドは PyFIE ポインタのクラスメソッドとなります。 引数 ptr_obj に渡されたポインタインスタンスから、 このクラスのポインタに変換(キャスト)したインスタンスを返します。

パラメータ:

ptr_obj -- 変換される PyFIE ポインタインスタンス

戻り値:

変換されたこのクラスの PyFIE ポインタインスタンスを返します

PTR.as_pycapsule()

このポインタと同じアドレスを指し示す PyCapsule インスタンスを返します。 このメソッドはポインタ型として PyCapsule インスタンスを要求するライブラリにて PyFIE ポインタ型を渡すために使用します。

戻り値:

このポインタと同じアドレスを指し示す PyCapsule インスタンス



ユーティリティ関数

pyfie.malloc(instance_cls, num)

パラメータ instance_cls で指定される PyFIE データ型を要素とし、 パラメータ num で指定された個数を要素数とする配列(メモリ領域)を確保し そのポインタを返します。

つまり下記バイトサイズのメモリ領域の確保が行われます。

sizeof(instance_cls) * num

本関数で確保されたメモリ領域は、 Python のガベージコレクション対象となるため 解放する必要はありません。

パラメータ:
  • instance_cls -- 要素となる PyFIE データ型のクラスを指定します

  • num -- 要素の個数を指定します

戻り値:

確保されたメモリ領域への PyFIE ポインタインスタンスを返します.

注釈

本関数により確保されたメモリ領域に対して、 PyFIE 関数 fnOAL_free() による解放や fnOAL_realloc() によるサイズ変更を行うことは出来ません。