使用2015cp範本練習".py"呼叫".pyd"。

編譯出Python的動態連結庫 :

https://github.com/coursemdetw/2015cp下載老師的範本練習。

C程式的部分分成pymod和pyfun。

這兩組底下都有3個Node,分別是編譯Python動態連結庫用的button、呼叫用的Python程式和一個C++編成的函式。

button裡的內容如下:

...
# compile
os.system("Z:/C/MinGW/bin/gcc.exe -c -g -IV:/IDE/Python33/include  -MMD -MP -MF build/"+target_name+".o.d -o build/"+target_name+".o "+filename)
# link 因為 .c 程式中起始為 PyInit_sum, 因此
os.system("Z:/C/MinGW/bin/gcc.exe -shared -o dist/"+module_name+".pyd build/"+target_name+".o V:/IDE/Python33/libs/libpython33.a")
g.es("done")

在執行時顯然對應不到W:槽的MinGW和Python34,所以必須改一下它的指令。

...
# compile
os.system("gcc -c -g -IW:/Python34/include  -MMD -MP -MF build/"+target_name+".o.d -o build/"+target_name+".o "+filename)
# link 因為 .c 程式中起始為 PyInit_sum, 因此
os.system("gcc -shared -o dist/"+module_name+".pyd build/"+target_name+".o W:/Python34/libs/python34.lib")
g.es("pymod done")

而另一邊的pyfun同理。

接著就能在\dist資料夾中編譯出sum.pyd和pyfun.pyd了。


使用mypy.py :

接著回到@edit programs/dist/mypy.py和mypy2.py的兩個Node,只要在.leo中存檔就能建立和編輯它們。

mypy.py中導入sum,並使用它裡面的函式sum()。

print(sum.sum(1, 30))

在sum.pyd中,函式sum()是sum2()在sum.pyd中被定義的外部名稱,由以下可知。

// 定義內部運算的函式內容
int sum2(int a, int b)
{
    return a+b;
}

// sum 函式的 interface
static PyObject* mod_sum(PyObject *self, PyObject *args)
{
    int a;
    int b;
    int s;
    // ii 表示兩個輸入變數都是整數
    if (!PyArg_ParseTuple(args,"ii",&a,&b))
       return NULL;
    // 這裡的 sum2 則是內部的函式定義, 與外部呼叫模組或函式名稱沒有直接關係
    s = sum2(a,b);
    // i 表示 s 為整數
    return Py_BuildValue("i",s);
}

// 這裡的 sum 是外部模組的呼叫名稱, 而 mod_sum 則是內部的呼叫名稱 (即 interface function)
// Mod_Methods 為函式 (方法) 結構定義
static struct PyMethodDef Mod_Methods[] = {
    {"sum", mod_sum, METH_VARARGS, "Description.."},
    {NULL,NULL,0,NULL}
};

sum2()函式回傳的是兩個輸入值(pyd內部名為a和b),所以mypy.py收到後會顯示在畫面上。

另一邊的mypy2.py是使用pyfun.pyd,不過有兩個檔案太麻煩了,乾脆讓mypy.py導入兩個pyd就好了。

這次是加入一段字串。

print(pyfun.pyfun("我的字串"))

在pyfun.pyd中,pyfun()的外部名稱和它同名,在它的函式中,建立了一個指標to_who,偵測使用動態連結庫的對象,並把輸入進來的字串傳回。

pyfun(PyObject *self,PyObject *args)
{
    const char *to_who;
    if(!PyArg_ParseTuple(args,"s",&to_who))
        return NULL;
     // pyfun 函式會將輸入字串變數傳回
    return PyUnicode_FromString(to_who);
}

執行後的結果:

不過這兩個pyd的結尾不太一樣。

sum.pyd

// 模組啟始, PyInit_ 後必須使用"名稱".pyd 中的模組名稱, 以便啟動
// 換言之, 若編譯連結後的動態模組名稱為 sum.pyd, 則此地的起始函式名稱必須為 PyInit_sum
PyMODINIT_FUNC
PyInit_sum(void)
{
    // 建立模組的起始, 輸入為模組結構名稱之 address
    (void) PyModule_Create(&ModMethods);
}

pyfun.pyd

// 此 pyd 必須命名為 pyfun.pyd, 因為必須與 PyInit_後的名稱相同
PyInit_pyfun(void)
{
    PyObject *m;
    m = PyModule_Create(&moduledef);
    return m;
}

意思似乎是一樣的,不過pyfun.pyd將PyModule_Create當作回傳值回傳了,不知道用意為何。

若要瞭解Python的函式用法,應該要查閱Python.h或是相關說明才能明白。


延伸應用 :

只使用既定的範本實在是太簡略了,所以試著在pyd中加一些自己寫的函式。

在sum中加入新的函式sum3,並讓它的傳回值設定為a和b相加後開平方根,再乘上10。

添加了下列指令:

//導入
#include "math.h"
//宣告
int sum3(int a, int b);
//副程式sum3
int sum3(int a, int b)
{
    return sqrt(a+b)*10;
}
//定義輸入值和sum3的外部名稱score
static PyObject* mod_score(PyObject *self, PyObject *args)
{
    int a;
    int b;
    int t;
    if (!PyArg_ParseTuple(args,"ii",&a,&b))
       return NULL;
    t = sum3(a,b);
    return Py_BuildValue("i",t);
}

並在PyMethodDef Mod_Methods[]中再新增一串:

{"score", mod_score, METH_VARARGS, "Description.."},

接著在mypy.py中呼叫sum.score()就行了。


Leo的搜尋功能和復原功能真的不太實在。常常搜尋時就跳到其他Node去,搜尋目標也要每次都重新調整;復原直接還原到存檔前的樣子,所以用它來編譯程式真的滿吃力的。

這次使用的是Python,所以SciTE和只能寫C語言的NetBeans幫不上忙,因為無法呼叫Python.h。說不定設定環境係數可以奏效?

不然只靠Leo編輯,它的函式分類也不是很清楚,常常不知道Python.h中的函式原型用法是甚麼,不像NetBeans可以連進去查看stdio.h或是windows.h所引用的函式。


Comments

comments powered by Disqus