饭中淹的避难所~~~~~

偶尔来避难的地方~

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  94 随笔 :: 0 文章 :: 257 评论 :: 0 Trackbacks
项目里需要使用脚本,我是框架的维护人员,就需要把脚本系统加入到框架里。
因为对PYTHON比较熟悉,所以最后选择了PYTHON作为脚本系统的主引擎。
下面是在这个过程中遇到的种种问题以及最后的解决方法。

1- 编译出错,提示找不到 __imp_Py_XXXX的引用
   这个问题真的很恶心,WIN版的PYTHON27.DLL用的是单线DLL的CRT,而项目如果用的是多线LIB的CRT,或者其他不兼容的CRT,就会出这个问题。
   果断下载PYTHON2.7.2的源代码,编译之。然而,编译过程也非常的恶心,工程属性修改成MTd和MT仍旧不行。
   后看到编译时有调用cl的命令行,找了半天在make_buildinfo这个项目里发现是使用代码生成的编译参数,里面写死的是MDd,修改之再编译,问题解决。

2- Py_Initialize 退出,提示无法找到site模块。
    这个问题原本想暴力的修改源代码里的nosite标记改之,后思考可能这个模块是有用的,所以把运行过PYTHON.EXE的PYTHON运行路径里的LIB里的被编译成PYC的所有PY按路径复制到EXE的路径下,问题解决。

3- 不想用BOOST.PYTHON,又想不用写PyCFunction的形式的C模块方法
    这个问题经过分析,得到结论是,从任意形式的用户函数生成一个PyObject*(*PYFUNC)(PyObject*,PyObject*)形式的函数封装。并且对于一个独立地址的用户函数,需要一个独立地址的函数封装。
    首先,我开始解决如何从一个固定函数得到另一个固定地址函数。一开始我想到用函数地址作为模版参数,这样每个独立地址就能生成一个单独的模版类,然后模版类里的静态函数自然就是独立地址的。后我使用了static的本地函数测试,未果,提示什么non-extra的错误。后进群询问,得到同样答案,并且编译成功的结果。仔细观察后发现,他用的是非static修饰的函数。我去掉static修饰,竟然成了。
    接下来,我开始解决如何从PyObject*args解析出用户函数的每个参数的问题。这个问题有几个部分,第一部分是如何从函数里析出每个参数,这部分我用了模版的某种特化,具体名字我不懂,就是如下面这种形式:
   
template <typename TR, typename T1 = void, typename T2 = void>
struct st_func {};
template 
<typename TR, typename T1>
struct st_func<TR, T1, void> {};

    这样就可以析出返回值TR和各个参数的类型。为了支持尽可能多的参数,我写了个程序生成了从0个参数到40个参数的模版变体。
    使用固定地址,因为函数定义需要TR,T1,所以函数指针无法直接在第一个模版参数传递,并且是固定参数,无法放在可选参数后面,所以我选择了先在第一个参数用void*传递函数指针,然后在st_func内部用TR,T1...这些拼出一个函数指针,把首位置的指针强转成原函数形式,再进行调用。
    因为PYTHON的解析函数需要每个参数的类型标识符,大部分类型是一个字符表示,这部分用了enum来为每个类型生成一个const且static的字符标识。比如
template <> struct type_char<int> { enum{ typechar = 'i' };};
    然后最终PyArg_ParseTuple 需要的是一个字符串,那么我在代码里就 这样来生成这个字符串:
   
char szTypes[] = { type_char<T1>::typechar, .., 0 };
    最后一个部分是解决TR是void时的函数返回值问题。后来我用了struct内部的特化函数来解决。最终形式如下:


template <void* FP, typename TR, typename T1>
struct st_cppfunc_to_py<FP, TR, T1, void, void>
{
typedef TR (
*TFP)(T1);
static PyObject * func( PyObject * self, PyObject * args ) { return _func<TR>( self, args ); }
template
<typename ITR>
static PyObject * _func( PyObject * self, PyObject * args ) {
char szType[] = { type_char<T1>::typechar, 0 };
T1 v1;
if( PyArg_ParseTuple( args, szType, &v1 ) ) {
TR ret
= ((TFP)FP)( v1 );
char szRetType[2] = { type_char<TR>::typechar, 0 };
return Py_BuildValue( szRetType, ret );
}
Py_RETURN_NONE;
}
template
<>
static PyObject * _func<void>( PyObject * self, PyObject * args ) {
char szType[] = { type_char<T1>::typechar, 0 };
T1 v1;
if( PyArg_ParseTuple( args, szType, &v1 ) ) {
((TFP)FP)( v1 );
}
Py_RETURN_NONE;
}

};

外面包一个  template <void*FP, typename TR, typename T1> PyCFunction __cppfunc2py( TR(*RFP)(T1) ) { return st_cppfunc_to_py<FP, TR, T1>::func; }

就可以很方便的生成嵌入PY的函数了。

4- 包装的scriptvalue怎么获得PyObject*的类型呢
    用Py_TYPE(ob)就可以获取到object的typeobject,它的tp_name就是它的名字,常用类型 int, long, float, str 都可以分辨出来。

5- PYTHON的调试版总是报GC异常
    这个问题我是尝试着来解决的,总结了以下几点:
    a. 模块的DICT是不用PY_XDECREF来释放的   
    b. 返回值需要一个PY_XDECREF释放。
    c. Py_BuildValue 返回值需要一个PY_XDECREF。
    d. 用户模块不需要 PY_XDECREF。
    e. PY文件生成的模块需要一个 PY_XDECREF。
    f. Set Object到Tuple去调完PY的函数,PY_XDECREF(TUPLE)时,需要注意的是,Set进去的Object都会被调用一次Py_XDECREF,所以一个好的办法是在Set进Tuple时,就INC一下他们的REF。
6- 为何一直调不到py文件里的函数
    这个问题困扰了我几分钟,模块的method获取不到,让我一度以为是脚本写的问题。
    后来我打印出来脚本的搜索路径(print sys.path),是一大堆的路径,我放进去的路径排在最后。于是我想,是不是有的路径下有重名的PY文件,就给文件改了个名字,结果就OK了。
    这个问题,我后来想可以通过调整搜索优先级来解决,不过目前还是这样解决比较好,因为调整方法目前还不得而知。

   
posted on 2012-02-10 21:55 饭中淹 阅读(2860) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理