EverET.org

好记性不如烂笔头

C++与Python混合编程

| Comments

混合语言策略可以汲取各语言之所长,让开发更加敏捷。混合语言策略在在应用得当时可以让程序更加优雅。

在《Unix编程艺术》中,Raymond说道:

混合语言是一种知识密集型(而不是编码密集型)的编程。要让它能够工作,我们不仅应该具备相当数量的多种语言应用知识,并且还需要具备能够判断这些语言在什么地方最适合、以及怎样把他们组合在一起的潜经验。

在混合语言编程中,我们遇到的第一个问题是如何需要让他们可以互相调用。也就是像C++可以调用Python的函数、Python又可以调用C++的函数。

对于C++和Python的混合编程主要有两种方式。

  1. 将C++写的模块编译成动态链接库,然后由Python主程序使用。这种一般是单方向的使用。

  2. 用C++主程序调用Python。然后Python中可以使用C++主程序的函数。

对于第一种方式非常简单,我们在此就不讨论了。我们将着重讨论第二种方式。

Simple Exampe of Mixing C++ and Python Code

我们举个例子。以下是C++主程序,做的事情是创建了一个空白的地图,然后加载Python模块构建地图。

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// website: http://EverET.org
#include <iostream>
#include <vector>
#include <boost/python.hpp>

class Map
{
public:
    Map() : m_map(10, std::vector<char>(20, '.'))
    {}

    void SetPixel(int x, int y, int val)
    {
  m_map[y][x] = val;
    }

    void Print()
    {
  for (int i = 0; i < m_map.size(); ++i)
  {
      for (int j = 0; j < m_map[i].size(); ++j)
      {
      std::cout << m_map[i][j];
      }
      std::cout << std::endl;
  }
    }

private:
    std::vector<std::vector<char> > m_map;
};

void InitPython()
{
    Py_Initialize();

    if(!Py_IsInitialized())
    {
  exit(-1);
    }
}

// Get the instance of the map, Singleton Pattern
// only one map instance exists
Map* GetMapInstance()
{
    static Map* the_map = NULL;
    if (!the_map)
    {
  the_map = new Map();
    }
    return the_map;
}

// export c++ function and class to python
BOOST_PYTHON_MODULE(MyEngine)
{
    using namespace boost::python;
    def("GetMapInstance", GetMapInstance,
  return_value_policy< reference_existing_object >());
    class_<Map>("Map", "Game Map")
      .def("Print", &Map::Print)
      .def("SetPixel", &Map::SetPixel,
      (arg("x"), "y", "val"));
}

int main()
{
    try
    {
  using namespace boost::python;

  InitPython();
  initMyEngine(); // init MyEngine Module

  // Add current path to sys.path. You have to
  // do this in linux. While in Windows,
  // current path is already in sys.path.
  object main_module = import( "__main__" );
  object main_namespace = main_module.attr( "__dict__" );
  object ignored = exec(
      "import sys\n"
      "sys.path.append('.')\n", main_namespace );

  Map* map = GetMapInstance();
  std::cout << "Before python\n";
  map->Print();

  // load python to design the map
  object mapMaker = import("mapmaker");
  object makeMap = mapMaker.attr("make_map");
  makeMap();

  std::cout << "\nAfter python\n";
  map->Print();
    }
    catch (...)
    {
  PyErr_Print();
    }

    return 0;
}

Python写的地图生成程序:mapmaker.py。

python
1
2
3
4
5
6
7
8
9
import MyEngine
import random

def make_map():
    the_map = MyEngine.GetMapInstance()
    n = 10
    for i in range(n):
        for j in range(10 - i, 10 + i - 1):
            the_map.SetPixel(j, i, ord('*'))

我们可以仔细看看C++和Python代码中被高亮的行(代码是js高亮的,如果还没高亮请稍等页面加载完成)。

C++通过Boost库可以方便地和Python交互。当然我们还可以直接是用解释器提供的C API的和Python交互,不过这样会有非常多的累赘的代码。

C++和Python交互的关键之处是通过BOOST_PYTHON_MODULE来导出C++的函数和类,此外还需要执行initMyEngine这个函数来将这个模块注册进Python解释器。具体细节可以见代码。

对于

1
2
def("GetMapInstance", GetMapInstance,
  return_value_policy< reference_existing_object >());

中的return_value_policy< reference_existing_object >()可以参考我以前写的:http://blog.csdn.net/cedricporter/article/details/6828322

编译:

g++ main.cpp -o map -I/usr/include/python2.7 -lboost_python -lpython2.7

在Windows上编译更容易,就不再罗嗦。

最后程序的输出,可以见到Python将地图由空白变成了一个三角形:

Example

对于用合适的语言来做合适的事情,会让开发效率和产品质量有所提高。例如,在Emacs里面,就可以是用lisp来控制emacs,AutoCAD中也可以是用脚本来绘图。这样的用户接口更加灵活。

Other

Scar

对于Scar这款3D太空:http://everet.org/2012/01/scar.html,是用C++和Python混合编程。在Scar中的2D界面库,有C++写成的基本元件,例如容器和按钮。我们可以在Python中组装C++元件来装配游戏的2D界面,然后返回一棵树的根节点给C++。于是像Scar中的水平仪,提示面板,地图,血条等等都是在Python中组装好的。此外,我们还可以在Python中编写元件的事件处理函数。这样做的好处是,我们在修改界面的时候,不再需要重新编译程序,只要修改脚本就好。

这样的开发会更加便捷而且应对变更的能力会更强。

ImaginationFactory

Imagination Factory是在大一的时候写的一个图像处理程序,http://everet.org/2012/01/imagination-factory.html

图像处理核心使用C++编写,界面使用C#/WPF编写。用WPF写界面即方便又便捷,可以轻松地实现很酷的效果。

Clover —— Computer Simulation Origami

Clover是一款计算机模拟折纸的程序,主要程序使用C#编写,内嵌Python解释器,可以用Python折纸。

本文链接: http://everet.org/cpp-python.html

您可能也喜欢

Comments