はじめに
c言語で作成したシェアードライブラリ(*.so)をpythonから呼び出す場合、
C言語の関数の呼び出し方さえ変わらなければ、処理の中身が変わっても、
pythonの処理(ソース)の変更を行うことなく、最新の状態で動作します。
残念なのは、#defineで定義している内容が変わった場合、
最悪、python側のソースを変更しないと期待しているように動かない
可能性がある事です。
いくつかオープンソースの実装例を見ていくと、C言語側で定義している#define xxx を、
pythonでは、同じ名称のインスタンスへの代入文で実装しています。
実際、同じ内容を2箇所(c,python)に持っている状況が気持ちが悪いですし、
値が変わったか気にするのも大変だし、値を転記するのも面倒なので、
何か仕組みができないかと考えた案を投稿します。
pythonは、1ヶ月位しか勉強していないので、このコーディング
がリーズナブルなのかわからないのですが、もっと良いやり方が
あれば、ぜひ教えてください。
使い方
C言語側条件
- 記号定数マクロのみ指定可能
- マクロ展開後に定数に展開できない場合は、使用できない(変数が含まれる場合など)
- マクロの入れ子(多重定義)も展開できる
以下の3つのマクロの値をpython側に渡したい場合
#define HOGE_CONST1 0xff123456
#define HOGE_CONST2 "[test string test]"
#define HOGE_CONST3 (132.6)
下記のように記述する。
GLV_CONST_DEFINE(HOGE_CONST1,uint32);
GLV_CONST_DEFINE(HOGE_CONST2,char_p);
GLV_CONST_DEFINE(HOGE_CONST3,float);
GLV_CONST_DEFINE(マクロ名、型);
型の名前は、ctypesの型定義名称から'c_'を外した以下の名称を指定できます。
型:size_t,int8,uint8,int16,uint16,int32,uint32,int64,uint64,char_p,float,double
HOGE_CONST1 = glv_linking_value('HOGE_CONST1')
HOGE_CONST2 = glv_linking_value('HOGE_CONST2')
HOGE_CONST3 = glv_linking_value('HOGE_CONST3')
print('HOGE_CONST1:',type(HOGE_CONST1),'{:x}'.format(HOGE_CONST1))
print('HOGE_CONST2:',type(HOGE_CONST2),HOGE_CONST2)
print('HOGE_CONST3:',type(HOGE_CONST3),HOGE_CONST3)
実装
struct c_const_value {
char *type;
union{
size_t c_size_t;
int8_t c_int8;
uint8_t c_uint8;
int16_t c_int16;
uint16_t c_uint16;
int32_t c_int32;
uint32_t c_uint32;
int64_t c_int64;
uint64_t c_uint64;
char *c_char_p;
float c_float;
double c_double;
}v;
};
#define GLV_CONST_DEFINE(a,b) GLV_CONST_DEFINE2(_##a,b,a)
#define GLV_CONST_DEFINE2(a,b,c) struct c_const_value glv_c_##a = {.type = "c_"#b , .v.c_##b = (c)}
pythonで代入する型と値を構造体で作成するマクロを定義しています。
例えば、GLV_CONST_DEFINE(HOGE_CONST1,uint32);と記述した場合、以下の処理に展開されます。
struct c_const_value glv_c__HOGE_CONST1 = {.type = "c_""uint32" , .v.c_uint32 = (0xff123456)};
from ctypes import *
class c_Structure(Structure):
pass
class c_Union(Union):
pass
# ------------------------------------------------------------------------------
class glv_c_interface_v(c_Union):
_fields_ = [("c_size_t", c_size_t),
("c_int8", c_int8),
("c_uint8", c_uint8),
("c_int16", c_int16),
("c_uint16", c_uint16),
("c_int32", c_int32),
("c_uint32", c_uint32),
("c_int64", c_int64),
("c_uint64", c_uint64),
("c_char_p", c_char_p),
("c_float", c_float),
("c_double", c_double)]
class glv_c_interface(c_Structure):
_fields_ = [("data_type", c_char_p),
("data", glv_c_interface_v)]
def glv_linking_value(name):
'''
get consttant data into shared library.
'''
address = getattr(glview, 'glv_c__' + name, None)
if address:
data = cast(address,POINTER(glv_c_interface)).contents
value = eval(b'data.data.' + data.data_type)
#print('data_type:',type(data.data_type),data.data_type)
#print('value:',type(value),value)
data_type = data.data_type.decode('utf-8')
if data_type == b'c_char_p':
value = value.decode('utf-8')
#
if 0:
if data_type in ['c_int8','c_int16','c_int32','c_int64',
'c_size_t','c_float','c_double','c_char_p']:
print("loading {} typeof({}):\t{:5}".format(name,type(value),value))
elif data_type in ['c_uint8','c_uint16','c_uint32','c_uint64']:
print("loading {} typeof({}):\t{:5}(0x{:x})".format(name,type(value),value,value))
#
return value
else:
print('error:',name,' is not found into shared library.')
return None
python側では、指定した名前のアドレスから、型の情報と値を取り出しています。
アプリに実装した処理を書いてあるので、dll等は、読み替えてください。