初学python的C/C++扩展

在Python基础教程和Python Cookbook这两本书上都有这部分的内容,两本书上也都有说有更好用更方便的方式来处理C/C++扩展的问题,不需要进行像下面这样“细致的操作”。如果这样,那还真没有写这一篇的意义了。不过在某书上看过类似这样的内容:你的后包上的备用工具,会让你在修理东西的时候更放心一些。学习这一部分的内容不算冗余,相比较于通信领域的冗余和一些我们编写的通常情况下都用不到的代码,这部分的内容可以让我们在处理问题的时候都一个选择。



环境: ubuntu,其实我想说的是linux。
      python 2.7,我自学的这几天功夫,只用了2.7和3.2,其他的没用过,用的不多,所以具体各版本间有什么差异,也就不能说什么了。


这里主要讲点在模块文件,或者说接口文件(其实我也不知道具体叫啥)中的一些东西。
=============================mytest.cpp(这个名字无所谓)===================================
#include <python2.7/Python.h> //有可能找不到Python.h,可以 install python-dev 解决,至于
                                //python2.7-dev 这样的细节,我就不知道了。。
                                // Python.h 是一定要在所有的标准头文件之前的,具体原因是
                                // Python.h 里会有一些预处理定义需要被先执行。
#include <iostream>

// 先来一个不需要参数的, 返回一个int的函数。
// static 的细节有待探索、探讨。 这个PrototypeFuncName 函数名只要自己不弄乱了,随便起。。
static PyObject* PrototypeFuncName(PyObject* self, PyObject *args)
{
    int testInt(222);
    std::cout<<"...in the PrototypeFuncName ..."<<std::endl;
    return (PyObject*)Py_BuildValue("i", testInt); // "x"是格式字符串,i:int, 将testInt按照
                                 // int的格式,构建返回值。
                                // 类似的还有 l : long, d : double, s : char*(string?)
                               // 所以说, Py_BuildValue 是一个用来构建返回结果的函数/方法(?)
}

// 然后, 因为目前我们不需要更多的扩展函数了, 所以接下来就是 定义 接口函数与原型函数的对应字典
//(?)了。 具体的科学的说法,我也懒得查证了,反正我是这么理解的。
static PyMethodDef MethodsInYourMedule[] = {
         {"theFuncNameYouCallInPython", PrototypeFuncName, METH_VARARGS}, // 有逗号
                        // 将调用的名字和原型函数的名字对应起来,所以说像个“字典”。
                       // theFuncNameYouCallInPython ,就是你在python里面调用这个模块的这个方法
                       // 的时候的那个点号后面的方法名,同样的,只要不弄乱了,随便起。
// METH_VARARGS 这个参数简单的说,就是用来说明这个函数如果需要参数的话,
// 需要用 PyArg_ParseTuple(),这个函数/方法,对参数进行解包。在后面的
// 例子,你会看到的。
         {NULL, NULL, 0, NULL} // 这里据说是有必要的,用来说明 这个“字典”定义完了。网上还有
// 版本是 {NULL, NULL}, 我觉得含有四个值的版本更科学一点,所以
//选择目前的这个版本,但是 两个值的版本目前也没有运行出错。。
}; // 这是个“字典”的定义,又不是函数的定义,当然有';' 啊

// 接下来,就是模块初始化了。
// 虽然以上写的东西可能什么的有意义的事情都没做,但至少它还是C++,所以,,,其实我也不是特别
// 清楚,但据说是有必要的,所以就加了 extern "C".
extern "C" PyMODINIT_FUNC initYourModule(void){
(void)Py_InitModule("YourModule", MethodsInYourModule);
} // 这里的 YourModule 和 initYourModule里的YourModule 是一致的。

ok,废话这么多,最主要的文件写完了,接下来就是编译了:
    g++ -fPIC -shared -o YourModule mytest.cpp  --> YourModule.so
把这个YourModule.so 放到一个你能import YourModule 的地方,然后你就可以试试看了:
    >>>import YourModule
    >>>print YourModule.theFuncNameYouCallInPython()
       ...in the PrototypeFuncName ...
       222
    >>>

嗯,level-0结束了。哎呀,写了好多废话。。。
OK ,接下来是 level-0.1,其实没那么夸张,还是收获应该是比0.1多的。
// 上面那个不接收参数的函数,基本没什么用,那么下面我们来写个一个,让我们自己的C/C++函数接受一
//  两个int参数的借口函数。
static PyObject* FuncLevelUp(PyObject* self, PyObject* args) //老样子
{
    int arg0, arg1;
    if( !PyArg_ParseTuple(args, "ii", &arg0, &arg1)){    // 在上面的METH_VARARGS 那里
                                // 提到了这个函数/方法。如其名,解元组/解包。                     
                                // 将args作为元祖,按照位置顺序,解包赋值给变量
                               // arg0,arg1,注意这里的 & .
                               // i for int; ii for int,int; ids for int,double,char*/string.
                              // 说到了按位置顺序的,那么自然还有按照关键字的,后面再说。
        return NULL;    // 发生异常了,当然需要报告出去,具体细节看后面的链接吧,因为这里
                          // 我也没看完。。
    }
    int ret = arg0 + arg1; //居然还是一个没什么用函数。。。
    return (PyObject*)Py_BuildValue("i",ret); // 这里就没有 & 了, 可以理解为Py_BuildValue
                                     // 是PyArg_ParseTuple 的在某种程度上的逆方法。
}

添加完 这个 FuncLevelUp 接口函数后,记得在接口函数与原型函数的对应“字典”里加上对这个方法的定义。
.... [] = {
    ... ,
   { "funcLevelUpCallInPython", FuncLevelUp, METH_VARARGS },
    ... 
}
然后重新编译一下,你就可以在 python里,用 YourModule.funcLevelUpCallInPython(1,1)验证1+1是不是等于2了。。。

前面说到了按照位置顺序解包并赋值,那么自然有按照关键字解包并赋值的方法了。
staic PyObject* FuncKeyWordParse(PyObject* self, PyObject* args, PyObject* keywords)
{
    int base, hehe;
    static char* keyword_dict[] = {"base","hehe",NULL}; // 又见NULL,是为了表示结束了么。
    if(!PyArg_ParseTupleAndKeywords(args, keywords, "ii", keyword_dict, &base, &hehe)){
         return NULL;             // 我对这里的理解是,对于元组args,用(PyObject*)keywords
                                 // 捕获python下提供关键字关系,映射到 keyword_dict[]指定
                                 // 的关键字关系,然后将 keyword_dict[0,1,2...]的值,按照
                                 // 格式字符串的需求,赋值给 &arg0,1.2...
// 注意,这里的解包顺序,还是会按照&arg的顺序,将_dict[]中的数据
                     // 依次序赋值,所谓的关键字,其实说的是在python里调用函数的时候
                     // 所使用的关键字的方式,提供参数。
    }
    std::cout<<power(base, hehe)<<std::endl; // 终于有高级一点的东西出现了。。
    return Py_None; // 返回一个Python的NULL?,也就是没有返回值了。
}
同样的,需要在“字典里”加上定义。
.... [] = {
    ... ,
   { "funcKeyWordParseCallInPython", FuncKeyWordParse, METH_VARARGS|METH_KEYWORDS, "testing.." },
    ... 
}   // METN_VARARGS | METH_KEYWORDS 应该可以说明关键字与顺序解包的问题了吧。
    // 后面那个"testing" , 嗯,待探索,探讨吧。


嗯,昨晚和今天下午就折腾了这么个东西。 应该有不少地方说的极其的不科学,没能体现出身为理工男应有的科学严谨的态度,,,嗯,就那样吧。其他内容,以后心情好的时候再慢慢说吧。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值