zoukankan      html  css  js  c++  java
  • boost.python入门教程 ----python 嵌入c++

    Python语言简介

           Python是一种脚本语言。以开放的开发接口和独特的语法著称。尽管Python在国内引起注意只有几年的时间,但实际上Python出现于上世纪90年代(据www.python.org介绍,这个时间可以上溯至1990年),已经有十几年的时间,它的流行也有很久,在嵌入脚本、互联网应用、系统管理和维护等领域,Python使用的非常广泛。

           Python的语法与我们常见的C系语法有很大不同,对于Python,书写格式也是语法的部分。主要表现在,每一个子语句段都要比它的父级缩进一层。例如:

    • >>> a, b = 0, 1
    • >>> while b < 1000:
    • ...     print b,
    • ...     a, b = b, a+b
    • ...

    以上是一段求Fibonacci数列的程序,>>>和…都是Python控制台的交互提示符。我们可以看到,while循环的循环体不是通过括号或者end之类的关键字标示,而是用缩进与上一级区分开。在Python中,每级代码之间的缩进距离不做要求,但是在同一个文件中必须保持一致,最常见的写法是缩进四个空格。

    理解这一点后,Python代码的可读性非常高。在强制性的书写规范下,更容易写出规范的代码。

    Python支持通用的各种语法结构,包括if、while/for循环、函数、类、异常处理以及模块(module)和包(Package)。以下是一些常见的语法示例:

    (展示Python代码)

    时间所限,我们不可能在这里详细了解Python语言,对于交互式开发人员,有几点值得注意,在这里简单介绍下:

    Python采用命名—对象实体机制。任何Python中的元素都可以看作是一种对象。包括所有的数据类型和定义。对变量的定义和赋值,实际上是将一个命名绑定到托管环境中的一个对象上。这个过程不涉及对象本身的运算,也不进行类型检查,所以在程序运行过程中可以任意对某个命名赋值并改变其类型。

    Python对象在运行期可以动态的添加和删除成员,不受类型限制(当然,也可以设计出静态类型的数据结构)。

    Python支持运算符重载。

    Python C API

           Python虚拟机开放了大量的API,几乎所有的语法内容,和虚拟机功能,都可以通过C-API从外部调用实现。加上整个虚拟机开放源码,用C语言操作Python虚拟机非常方便,从某种意义上说,Python可以作为一个C语言的运行时环境存在。同时,C语言也可以作为Python的底层扩展使用。

           Python C API的引用头文件是python.h,在Python安装目录的include子目录可以找到。静态连接库pythonxx.lib(xx=主版本号+一级子版本号,如python2.4对应python24.lib)在libs目录下。

    扩展Python系统

           扩展Python,即编写可供Python加载的模块,由Python程序进行调用。简单来讲,主要有以下几个步骤:

           编写符合Python封装约定的函数和结构定义。对于函数而言,要求参数均为PyObject*

    或其“子”类型。当然,C语言中没有这样的语法,这个继承关系是Python语法意义的。而对于自定义的对象,Python有一套完备的约定,我们在Python的文档中可以看到若干示例。(展示代码)。

           对于需要开放的函数,要编写方法定义表,这是一个UNION,按照固定格式填写数据。(展示代码)。对于类型对象,则是通过PyModule_AddObject方法引入。

           要引入函数定义的话,还需要执行模块初始化函数。

           将项目编译为动态链接库,放在Python的DLLs路径即可。

    嵌入Python解释器

           在C程序中嵌入Python解释器并不复杂,基本的流程如下:

           调用Py_Initialize()函数,初始化虚拟机。

           编写操作虚拟机的命令。

           调用Py_Finalize()函数释放虚拟机资源。

           Python虚拟机提供了丰富的API以供操作,基本上用Python脚本可以实现的功能,通过API都可以实现。当然通常不会完全使用API,而是以API和内嵌脚本结合使用,在Python文档中提供了一个简单的例子,演示了常用的模块和对象引入、执行可回调对象,变量提取等功能(展示代码)。

    综合技术——扩展式嵌入

           我们可以将Python虚拟机嵌入C/C++程序的同时,通过扩展代码,将我们需要的内容引入Python虚拟机,这样,就可以在虚拟机中通过Python脚本访问C++环境中提供的扩展内容,在掌握了扩展和嵌入技术后,这个应用十分自然和简便,在Python文档中,有一个实例说明了这中应用方式。(展示代码)

    Boost.Python

           Python虚拟机本身以标准C编写,它的API也均为C型式,直接应用在C++程序中略有不便。通常我们会使用一些第三方的程序库来实现,这里重点介绍BOOST.Python的使用。

           Boost是一个久负盛名的C++代码库,关于它的具体情况在网上有很多介绍,具体到BOOST.Python,这是BOOST开发组专门为C++/Python直接的互操作而开发的类库,也是Boost中唯一一个解释语言支持模块。Boost.Python除了提供Python C API的C++兼容封装,也使得整个扩展/嵌入过程更加方便。

    BOOST的预编译

           BOOST中的部分模块需要预编译才能使用,Boost.Python也在此列。在Boost的文档中,有详细的编译方式说明,具体到我们使用的VC6/7.1,windows 2000+,Python2.4,编译过程可以简化为以下几步:

    1、  生成Boost编译工具BJam。这个工具的源码在Boost目录的tools/build/jam_src 目录下,正常情况下,直接执行build.bat即可生成。执行完毕后,到bin.ntx86子目录找到bjam.exe文件,复制到Boost根目录即可。

    2、  配置编译环境,编译Boost时,要提供编译器种类和路径,子模块还可能有自己的配置要求,如Python子模块要求提供Python的目录和版本号。

    3、  执行Bjam。此时要向Bjam提供使用的编译器种类,以及附带的编译参数。

    4、  以下是我编译Boost使用的bat文件,大家需要编译Boost的话,可以将其中的VC和Python路径改为本机路径,即可使用。

    bjam_vc71.bat

    set PYTHON_ROOT=D:/Python24

    set vc-7_1=D:/Microsoft Visual Studio .NET 2003/Vc7

    set ICU_PATH=D:/icu3.4

    set VC71_ROOT=D:/Microsoft Visual Studio .NET 2003/Vc7

    bjam -sTOOLS=vc-7_1 -sICU_PATH=D:/icu3.4 stage

    bjam_vc60.bat

    set PYTHON_ROOT=D:/Python24

    set ICU_PATH=D:/icu3.4

    set MSVC_ROOT=D:/Microsoft Visual Studio/VC98

    bjam -sTOOLS=msvc -sICU_PATH=D:/icu3.4 stage

    这里的ICU是一个国际化代码库,Boost使用这个第三方代码库为自己的正则表达式模块提供Unicode支持,如果只使用Python模块,可以去掉这方面的配置。

    Stage参数指示编译工具将生成的lib文件统一放置于stage子目录。

    Boost.Python的引用头文件是<boost/python.hpp>,命名空间是boost::python。

    扩展Python系统

           首先,对于Python基于对象的体系结构,Boost提供了一组封装对象给予支持。这其中除了基本的PyObject(Boost中封装为Boost::Python::object) 对象,还包括了序列容器组。Boost.Python文档的“Obejct Wrapper”部分(/libs/python/doc/v2/ObjectWrapper.html#ObjectWrapper-concept)对此有详细讲解。如果我们的扩展代码中需要调用这些对象,可以直接引用。

           另一方面,对于函数和对象封装,Boost提供了一套更为友好的方式。例如,如果在函数声明的参数列表中出现可以与Python类型直接对应的char *、int、float等,不需要手动进行与PyOjbect*的转化,Boost可以识别这些类型。

           下面我们看一个简单的函数封装示例:

           首先,在VC中建立一个空的DLL项目,然后定义函数:

          

    Cppcode.h

    #include <string>

     

    int Add(int, int);

     

    int Sub(int, int);

     

    std::string Concat(std::string, std::string);

     

    Cppcode.cpp

    #include "CppCode.h"

     

    int Add(int x, int y)

    {

         return x + y;

    }

     

    int Sub(int x, int y)

    {

         return x - y;

    }

     

    std::string Concat(std::string x, std::string y)

    {

         return x + y;

    }

     

    然后编写封装代码:

    Wrapper.cpp

    #include <boost/python.hpp>

    #include "CppCode.h"

     

    using namespace boost::python;

     

    BOOST_PYTHON_MODULE(FunTest)

    {

         def("Add", Add);

         def("Sub", Sub);

         def("Concat", Concat);

    }

    编译生成DLL文件(需要注意的是生成的文件名必须与BOOST_PYTHON_MOUDLE宏的参数一致),放在Python/DLLs目录下即可。(演示)

    由于Python使用动态类型,对于Python代码,函数重载没有语法依据(但是可以定义默认参数值和动态参数列表)。但我们在C++代码中可能会需要重载多个函数定义,Boost.Python对此也有支持。(展示代码)

    下面我们再看一个C++类型的封装代码:

    Wrapper.cpp

    ///////////////////////////////////////////////////////////

    //MultiBoolean类的Python封装,使用boost::python技术

    //作者:刘鑫

    ///////////////////////////////////////////////////////////

     

    //引用boost库

    #include <boost/python.hpp>

    //引用多值逻辑定义

    #include "MultiBoolean.h"

    //引用与或运算的Python封装接口

    #include "pyMultiBoolean.h"

     

    //引用相关的命名空间

     

    using namespace boost::python;

     

    using namespace MarchLibrary;

     

    //模块定义

    BOOST_PYTHON_MODULE(MarchLibrary)

    {

         //类封装,这里用no_init表示没有可用的构造函数

         class_<MultiBoolean>("MultiBoolean", no_init)

              //可选的逻辑值接口

             .def_readonly("True", &MultiBoolean::True)

             .def_readonly("False", &MultiBoolean::False)

             .def_readonly("Unknown", &MultiBoolean::Unknown)

             .def_readonly("Undefine", &MultiBoolean::Undefine)

             .def_readonly("Nil", &MultiBoolean::Nil)

     

             //兼容C++版定义的字符串处理接口

             .def("ToString", &MultiBoolean::ToString)

             .def("FullName", &MultiBoolean::FullName)

             .def("Parse", &MultiBoolean::Parse)

     

             //逻辑值判定

             .def("IsTrue", &MultiBoolean::IsTrue)

             .def("IsFalse", &MultiBoolean::IsFalse)

             .def("IsUnknown", &MultiBoolean::IsUnknown)

             .def("IsUndefine", &MultiBoolean::IsUndefine)

             .def("IsNil", &MultiBoolean::IsNil)

     

             //运算符重载接口

             .def(!self)

             .def(self &= self)

             .def(self &= bool())

             .def(self |= self)

             .def(self |= bool())

             .def(self ^= self)

             .def(self ^= bool())

             .def(self == self)

             .def(self == bool())

             .def(bool() == self)

             .def(self != self)

             .def(self != bool())

             .def(bool() != self)

             .def(self & self)

             .def(self & bool())

             .def(bool() & self)

             .def(self | self)

             .def(self | bool())

              .def(bool() | self)

             .def(self ^ self)

             .def(self ^ bool())

             .def(bool() ^ self)

     

             //提供给Python解释器的标准字符串输出接口

             .def("__str__", &MultiBoolean::ToString)

             .def("__repr__", &MultiBoolean::FullName)

         ;

    }

    MultiBoolean本身是一个多值逻辑类型,它的实现比较长,就不在这里放出了。(直接在IDE展示)这里主要给大家演示下class封装的基本功能,包括方法、属性和运算符重载。

    下面我们讨论下继承的封装。我们知道C语言没有虚函数的概念,而Python的方法默认都是虚方法。为了实现这一功能,Python API中运用了一些复杂的方法。在Boost中,这个过程被尽可能的封装起来,向自然的C++代码靠拢。

    VirtualTest.h

    #include <boost/python.hpp>

     

    using namespace boost::python;

     

    //Override

    struct Base

    {

         virtual ~Base(){};

         virtual char const* Hello()

         {

             return "Hello. I'm Base.";

         };

    };

     

     

    struct Derived : Base

    {

         char const* Hello()

         {

             return "Hello. I'm Derived.";

         };

    };

     

    struct BaseWrapper : Base, wrapper<Base>

    {

         char const* Hello()

        {

             if (override Hello = this->get_override("Hello"))

    #if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) // Workaround for vc6/vc7

                  return call<char *>(Hello.ptr());

    #else

                return Hello();

    #endif

            return Base::Hello();

        }

     

         char const* default_Hello() { return this->Base::Hello(); }

    };

    Wrapper.cpp

    #include <boost/python.hpp>

    #include "ClassTest.h"

    #include "VirtualTest.h"

    #include <string>

     

    using namespace boost::python;

     

    BOOST_PYTHON_MODULE(ClassTest)

    {

         class_<Foo>("Foo")

             .def("Add", &Foo::Add)

              .def("Sub", &Foo::Sub)

             .def("Concat", &Foo::Concat)

         ;

     

         class_<ConFoo>("ConFoo")

             .def(init<char *>())

             .def("GetValue", &ConFoo::GetValue)

             .def("SetValue", &ConFoo::SetValue)

         ;

     

         class_<BaseWrapper, boost::noncopyable>("Base")

             .def("Hello", &Base::Hello, &BaseWrapper::default_Hello)

         ;

     

         class_<Derived>("Derived")

             .def("Hello", &Derived::Hello)

         ;

    }

     

     

    这里我们要关注的是用于虚函数定义的辅助类BaseWrapper,以及BOOST_PYTHON_MODULE内部的封装代码。

    以上两个示例也展示了构造函数的封装方法,包括禁用构造函数的用法。

    嵌入Python解释器

           最简单的Boost嵌入与Python C API并无任何不同,例如以下的代码:

    #include "boost/python.hpp"

     

    int main(int argc, char *argv[])

    {

         Py_Initialize();

     

         FILE * fp = fopen("test.py", "r");

     

         if (fp == NULL) {

             return 1;

         }

     

         PyRun_SimpleFile(fp, "test.py");

     

        Py_Finalize();

     

     

        return 0;

    }

           从以上可以看出,Boost沿用了C API的嵌入流程。但是,Boost在具体的虚拟机功能调用方面提供了完备的封装,以下这个示例展示了模块和对象引用,以及类型转换方面的使用。

    #include <boost/python.hpp>

     

    using namespace boost::python;

     

    int main()

    {

         Py_Initialize();

     

         object main_module((

             handle<>(borrowed(PyImport_AddModule("__main__")))));

     

         object main_namespace = main_module.attr("__dict__");

     

         handle<> ignored((PyRun_String(

     

             "hello = file('hello.txt', 'w')/n"

             "hello.write('Hello world!')/n"

             "hello.close()"

     

             , Py_file_input

             , main_namespace.ptr()

             , main_namespace.ptr())

         ));

     

         Py_Finalize();

    }

    对于其他功能,例如方法调用、成员访问等,Boost也提供了相应的功能。下面我们看一下Boost文档中的示例。(展示文档)

    综合技术——扩展式嵌入

    和C API一样,在掌握了扩展和嵌入代码后,扩展式嵌入技术就成为一件水到渠成的事,下面这个简单的示例,利用前面提到的MultiBoolean,演示了最简单的扩展式嵌入。

    #include <iostream>

    #include <cstdlib>

    #include <string>

    #include <boost/python.hpp>

     

    #include "main.h"

     

    using namespace boost::python;

     

     

    int main()

    {

         std::string dictstr;

         Py_Initialize();

         try

         {

             initMarchLibrary();

     

             object main_module((

                  handle<>(borrowed(PyImport_AddModule("__main__")))));

     

             object main_namespace = main_module.attr("__dict__");

     

             PyRun_SimpleString("import MarchLibrary");

             object result(( handle<>(

                       PyRun_String(

                                "str(dir(MarchLibrary))",

                                Py_eval_input,

                                main_namespace.ptr(),

                                main_namespace.ptr()))

                  ));

     

             dictstr = extract<char *>(result);

             std::cout<<dictstr<<std::endl;

         }

         catch(error_already_set)

         {

             PyErr_Print();

         }

     

         std::system("Pause");

         Py_Finalize();

         return 0;

    }

    注意引入MarchLibrary库的init函数,它于C API中使用的相同。程序运行时,我们可以看到dir函数打印出了该模块的内部成员,如同它是一个标准的Python内置模块。

  • 相关阅读:
    hdu 1214 圆桌会议(规律)
    hdu 1280 前m大的数
    hdu 2114 Calculate S(n)
    hdu 1210 Eddy's 洗牌问题
    hdu 1423 Greatest Common Increasing Subsequence(最长公共递增子序列lcis)
    7.30前端之Html简介
    8.3前端之Html列表
    7.30前端之Html头部
    7.30前端之Html元素
    7.23Java之递归练习
  • 原文地址:https://www.cnblogs.com/liaocheng/p/4263511.html
Copyright © 2011-2022 走看看