LoginSignup
0
0

More than 1 year has passed since last update.

pythonとc言語間での定数の同期方法の提案

Posted at

はじめに

c言語で作成したシェアードライブラリ(*.so)をpythonから呼び出す場合、
C言語の関数の呼び出し方さえ変わらなければ、処理の中身が変わっても、
pythonの処理(ソース)の変更を行うことなく、最新の状態で動作します。

残念なのは、#defineで定義している内容が変わった場合、
最悪、python側のソースを変更しないと期待しているように動かない
可能性がある事です。

いくつかオープンソースの実装例を見ていくと、C言語側で定義している#define xxx を、
pythonでは、同じ名称のインスタンスへの代入文で実装しています。

実際、同じ内容を2箇所(c,python)に持っている状況が気持ちが悪いですし、
値が変わったか気にするのも大変だし、値を転記するのも面倒なので、
何か仕組みができないかと考えた案を投稿します。

pythonは、1ヶ月位しか勉強していないので、このコーディング
がリーズナブルなのかわからないのですが、もっと良いやり方が
あれば、ぜひ教えてください。

使い方

C言語側条件

  • 記号定数マクロのみ指定可能
  • マクロ展開後に定数に展開できない場合は、使用できない(変数が含まれる場合など)
  • マクロの入れ子(多重定義)も展開できる

以下の3つのマクロの値をpython側に渡したい場合

c言語(連携したい値)
#define HOGE_CONST1	0xff123456
#define HOGE_CONST2	"[test string test]"
#define HOGE_CONST3	(132.6)

下記のように記述する。

c言語(連携インターフェース)
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

python
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)

実装

c言語
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)};
python
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等は、読み替えてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0