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

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

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

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

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

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

# 使い方

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


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

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

下記のように記述する。


```c: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: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: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);と記述した場合、以下の処理に展開されます。

```c
struct c_const_value glv_c__HOGE_CONST1 = {.type = "c_""uint32" , .v.c_uint32 = (0xff123456)};
```

```python: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等は、読み替えてください。