Skip to content

Commit

Permalink
update chapter 9
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoweiChen committed Oct 19, 2019
1 parent 90cf3f7 commit 1d55f3d
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 47 deletions.
6 changes: 3 additions & 3 deletions content/chapter9/9.0-chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
* 使用pybind11构建C++和Python项目
* 使用Python CFFI混合C,C++,Fortran和Python

有很多的库比较适合特定领域的任务。我们的库直接使用这些专业库,是一中快捷的方式,这样就可以使用来自其他专家组的多年经验进行开发。随着计算机体系结构和编译器的发展,编程语言也在不断发展。几年前,大多数科学软件都是用Fortran语言编写的,而现在,CC++和解释语言——Python——正占据着语言中心舞台。将编译语言代码与解释语言的代码集成在一起,变得确实越来越普遍,这样做有以下好处:
有很多的库比较适合特定领域的任务。我们的库直接使用这些专业库,是一中快捷的方式,这样就可以使用来自其他专家组的多年经验进行开发。随着计算机体系结构和编译器的发展,编程语言也在不断发展。几年前,大多数科学软件都是用Fortran语言编写的,而现在,C/C++和解释语言Python正占据着语言中心舞台。将编译语言代码与解释语言的代码集成在一起,变得确实越来越普遍,这样做有以下好处:

* 用户可以需要进行定制和扩展功能,以满足需求。
* 可以将Python等语言的表达能力与编译语言的性能结合起来,后者在内存寻址方面效率接近于极致,达到两全其美的目的。

正如我们之前的示例中展示的那样,可以使用`project`命令通过`LANGUAGES`关键字设置项目中使用的语言。CMake支持许多(但不是所有)编译的编程语言。从CMake 3.5开始,各种风格的汇编(如ASM-ATT,ASM,ASM-MASM和ASM- NASM)、C、C++、Fortran、Java、RC (Windows资源编译器)和Swift都可以选择。CMake 3.8增加了对另外两种语言的支持:C#和CUDA(请参阅发布说明:https://cmake.org/cmake/help/v3.8/release/3.8.html#languages )。
正如之前的示例中展示的那样,可以使用`project`命令通过`LANGUAGES`关键字设置项目中使用的语言。CMake支持许多(但不是所有)编译的编程语言。从CMake 3.5开始,各种风格的汇编(如ASM-ATT,ASM,ASM-MASM和ASM- NASM)、C、C++、Fortran、Java、RC (Windows资源编译器)和Swift都可以选择。CMake 3.8增加了对另外两种语言的支持:C#和CUDA(请参阅发布说明:https://cmake.org/cmake/help/v3.8/release/3.8.html#languages )。

本章中,我们将展示如何以一种可移植且跨平台的方式集成用不同编译(CC++和Fortran)和解释语言(Python)编写的代码。我们将展示如何利用CMake和一些工具集成不同编程语言。
本章中,我们将展示如何以一种可移植且跨平台的方式集成用不同编译(C/C++和Fortran)和解释语言(Python)编写的代码。我们将展示如何利用CMake和一些工具集成不同编程语言。
12 changes: 6 additions & 6 deletions content/chapter9/9.1-chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Fortran作为高性能计算语言有着悠久的历史。目前,许多线性

## 准备工作

第7章中,我们把项目结构列为一个树。每个子目录都有一个CMakeLists.txt文件,其中包含与该目录相关的指令。这使我们可以对子目录进行限制中,如这个例子:
第7章中,我们把项目结构列为一个树。每个子目录都有一个`CMakeLists.txt`文件,其中包含与该目录相关的指令。这使我们可以对子目录进行限制中,如这个例子:

```shell
.
Expand Down Expand Up @@ -63,7 +63,7 @@ end module

## 具体实施

我们有4个CMakeLists.txt实例要查看——根目录下1个,子目录下3个。让我们从根目录的CMakeLists.txt开始:
我们有4个`CMakeLists.txt`实例要查看——根目录下1个,子目录下3个。让我们从根目录的`CMakeLists.txt`开始:

1. 声明一个Fortran和C的混合语言项目:

Expand All @@ -82,7 +82,7 @@ end module
${CMAKE_CURRENT_BINARY_DIR}/modules)
```

3. 接下来,我们进入第一个子CMakeLists.txt,添加`src`子目录:
3. 接下来,我们进入第一个子`CMakeLists.txt`,添加`src`子目录:

```cmake
add_subdirectory(src)
Expand Down Expand Up @@ -132,7 +132,7 @@ end module
)
```

`utils`子目录中,还有一个CMakeLists.txt,其只有一单行程序:我们创建一个新的库目标,子目录中的源文件将被编译到这个目标库中。并与这个目标没有依赖关系:
`utils`子目录中,还有一个`CMakeLists.txt`,其只有一单行程序我们创建一个新的库目标,子目录中的源文件将被编译到这个目标库中。并与这个目标没有依赖关系:

```cmake
add_library(utils SHARED util_strings.f90)
Expand All @@ -146,7 +146,7 @@ add_library(utils SHARED util_strings.f90)
add_executable(bt-randomgen-example bt-randomgen-example.f90)
```

2. 最后,将在子CMakeLists.txt中生成的库目标,并链接到可执行目标:
2. 最后,将在子`CMakeLists.txt`中生成的库目标,并链接到可执行目标:

```cmake
target_link_libraries(bt-randomgen-example
Expand All @@ -171,7 +171,7 @@ function backtrace(buffer, size) result(bt) bind(C, name="backtrace")
* 列出的源文件中获取目标文件,并识别要使用哪个编译器。
* 选择适当的链接器,以便构建库(或可执行文件)。

CMake如何决定使用哪个编译器?在`project`命令时使用参数`LANGUAGES`指定,这样CMake会检查系统上给定语言编译器。当使用源文件列表添加目标时,CMake将根据文件扩展名选择适当地编译器。因此,以`.c`结尾的文件使用C编译器编译,而以`.f90`结尾的文件(如果需要预处理,可以使用`.F90`)将使用Fortran编译器编译。类似地,对于C++, `.cpp``.cxx`扩展将触发`C++`编译器。我们只列出了CC++和Fortran语言的一些可能的、有效的文件扩展名,但是CMake可以识别更多的扩展名。如果您的项目中的文件扩展名,由于某种原因不在可识别的扩展名之列,该怎么办?源文件属性可以用来告诉CMake在特定的源文件上使用哪个编译器,就像这样:
CMake如何决定使用哪个编译器?在`project`命令时使用参数`LANGUAGES`指定,这样CMake会检查系统上给定语言编译器。当使用源文件列表添加目标时,CMake将根据文件扩展名选择适当地编译器。因此,以`.c`结尾的文件使用C编译器编译,而以`.f90`结尾的文件(如果需要预处理,可以使用`.F90`)将使用Fortran编译器编译。类似地,对于C++, `.cpp``.cxx`扩展将触发`C++`编译器。我们只列出了C/C++和Fortran语言的一些可能的、有效的文件扩展名,但是CMake可以识别更多的扩展名。如果您的项目中的文件扩展名,由于某种原因不在可识别的扩展名之列,该怎么办?源文件属性可以用来告诉CMake在特定的源文件上使用哪个编译器,就像这样:

```cmake
set_source_files_properties(my_source_file.axx
Expand Down
4 changes: 2 additions & 2 deletions content/chapter9/9.2-chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
└── CxxLAPACK.hpp
```

这里,收集了BLAS和LAPACK的所有包装器,它们提供了`src/math`下的数学库了,主要程序为` linear-algebra.cpp`。因此,所有源都在`src`子目录下。我们还将CMake代码分割为三个CMakeLists.txt文件,现在来讨论这些文件。
这里,收集了BLAS和LAPACK的所有包装器,它们提供了`src/math`下的数学库了,主要程序为` linear-algebra.cpp`。因此,所有源都在`src`子目录下。我们还将CMake代码分割为三个`CMakeLists.txt`文件,现在来讨论这些文件。

## 具体实施

这个项目混合了C++(作为该示例的主程序语言)和C(封装Fortran子例程所需的语言)。在根目录下的CMakeLists.txt文件中,我们需要做以下操作:
这个项目混合了C++(作为该示例的主程序语言)和C(封装Fortran子例程所需的语言)。在根目录下的`CMakeLists.txt`文件中,我们需要做以下操作:

1. 声明一个混合语言项目,并选择C++标准:

Expand Down
8 changes: 4 additions & 4 deletions content/chapter9/9.3-chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private:

使用这个示例代码,我们可以创建余额为零的银行帐户。可以在帐户上存款和取款,还可以使用`get_balance()`查询帐户余额。余额本身是`Account`类的私有成员。

我们的目标是能够直接从Python与这个C++类进行交互——换句话说,在Python方面,我们希望能够做到这一点:
我们的目标是能够直接从Python与这个C++类进行交互换句话说,在Python方面,我们希望能够做到这一点:

```python
account = Account()
Expand All @@ -49,7 +49,7 @@ account.withdraw(50.0)
balance = account.get_balance()
```

为此,需要一个Cython接口文件(我们将调用这个文件`account.pyx`):
为此,需要一个Cython接口文件(调用`account.pyx`):

```python
# describe the c++ interface
Expand Down Expand Up @@ -79,7 +79,7 @@ cdef class pyAccount:

如何生成Python接口:

1. CMakeLists.txt定义CMake依赖项、项目名称和语言:
1. `CMakeLists.txt`定义CMake依赖项、项目名称和语言:

```cmake
# define minimum cmake version
Expand Down Expand Up @@ -186,7 +186,7 @@ cdef class pyAccount:

## 工作原理

本示例中,我们使用一个相对简单的CMakeLists.txt文件对接了Python和C++,但是是通过使用`FindCython.cmake`进行的实现。`UseCython.cmake`模块,放置在`cmake-cython`下。这些模块包括使用以下代码:
本示例中,使用一个相对简单的`CMakeLists.txt`文件对接了Python和C++,但是是通过使用`FindCython.cmake`进行的实现。`UseCython.cmake`模块,放置在`cmake-cython`下。这些模块包括使用以下代码:

```cmake
# directory contains UseCython.cmake and FindCython.cmake
Expand Down
20 changes: 10 additions & 10 deletions content/chapter9/9.4-chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Boost库为C++代码提供了Python接口。本示例将展示如何在依赖于

## 准备工作

在保持`account.cpp`不变的同时,修改前一个示例中的接口文件(`account.hpp`):
保持`account.cpp`不变的同时,修改前一个示例中的接口文件(`account.hpp`):

```c++
#pragma once
Expand Down Expand Up @@ -40,7 +40,7 @@ BOOST_PYTHON_MODULE(account)

## 具体实施

以下,是如何在C++项目中使用Boost.Python的步骤:
如何在C++项目中使用Boost.Python的步骤:

1. 和之前一样,首先定义最低版本、项目名称、支持语言和默认构建类型:

Expand All @@ -62,7 +62,7 @@ BOOST_PYTHON_MODULE(account)
endif()
```

2. 本示例中,依赖Python和Boost库,以及使用Python进行测试。Boost.Python组件依赖于Boost版本和Python版本,因此我们需要对这两个组件的名称进行检测
2. 本示例中,依赖Python和Boost库,以及使用Python进行测试。Boost.Python组件依赖于Boost版本和Python版本,因此需要对这两个组件的名称进行检测

```cmake
# for testing we will need the python interpreter
Expand Down Expand Up @@ -174,7 +174,7 @@ find_package(PythonInterp REQUIRED)
find_package(PythonLibs ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} EXACT REQUIRED)
```

注意,首先搜索解释器,然后搜索开发头和库。此外,对`PythonLibs`的搜索要求开发头文件和库的主版本和次版本,与解释器的完全相同。但是,命令组合不能保证找到完全匹配的版本。
首先搜索解释器,然后搜索开发头和库。此外,对`PythonLibs`的搜索要求开发头文件和库的主版本和次版本,与解释器的完全相同。但是,命令组合不能保证找到完全匹配的版本。

定位Boost.Python时,我们试图定位的组件的名称既依赖于Boost版本,也依赖于我们的Python环境。根据Boost版本的不同,可以调用python、python2、python3、python27、python36、python37等等。我们从特定的名称搜索到更通用的名称,已经解决了这个问题,只有在没有找到匹配的名称时才会失败:

Expand Down Expand Up @@ -203,15 +203,15 @@ endif()

可以通过设置额外的CMake变量,来调整Boost库的使用方式。例如,CMake提供了以下选项:

* Boost_USE_STATIC_LIBS:设置为ON之后,可以使用静态版本的Boost库。
* Boost_USE_MULTITHREADED:设置为ON之后,可以切换成多线程版本。
* Boost_USE_STATIC_RUNTIME:设置为ON之后,可以在C++运行时静态的连接不同版本的Boost库。
* **Boost_USE_STATIC_LIBS**:设置为ON之后,可以使用静态版本的Boost库。
* **Boost_USE_MULTITHREADED**:设置为ON之后,可以切换成多线程版本。
* **Boost_USE_STATIC_RUNTIME**:设置为ON之后,可以在C++运行时静态的连接不同版本的Boost库。

此示例的另一个特点是使用`add_library`的模块选项。我们已经从第1章第3节了解到,CMake接受以下选项作为`add_library`的第二个有效参数:

* STATIC:创建静态库,也就是对象文件的存档,用于链接其他目标时使用,例如:可执行文件
* SHARED:创建共享库,也就是可以动态链接并在运行时加载的库
* OBJECT:创建对象库,也就是对象文件不需要将它们归档到静态库中,也不需要将它们链接到共享对象中
* **STATIC**:创建静态库,也就是对象文件的存档,用于链接其他目标时使用,例如:可执行文件
* **SHARED**:创建共享库,也就是可以动态链接并在运行时加载的库
* **OBJECT**:创建对象库,也就是对象文件不需要将它们归档到静态库中,也不需要将它们链接到共享对象中

`MODULE`选项将生成一个插件库,也就是动态共享对象(DSO),没有动态链接到任何可执行文件,但是仍然可以在运行时加载。由于我们使用C++来扩展Python,所以Python解释器需要能够在运行时加载我们的库。使用`MODULE`选项进行`add_library`,可以避免系统在库名前添加前缀(例如:Unix系统上的lib)。后一项操作是通过设置适当的目标属性来执行的,如下所示:

Expand Down
18 changes: 9 additions & 9 deletions content/chapter9/9.5-chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@ PYBIND11_MODULE(account, m)
}
```

我们将按照pybind11文档的方式,通过CMake构建(https://pybind11.readthedocs.io/en/stable/compile )。并使用`add_subdirectory`将pybind11导入项目。但是,不会将pybind11源代码显式地放到项目目录中,而是演示如何在配置时使用`FetchContent` (https://cmake.org/cmake/help/v3.11/module/FetchContent.html )。
按照pybind11文档的方式,通过CMake构建(https://pybind11.readthedocs.io/en/stable/compile )。并使用`add_subdirectory`将pybind11导入项目。但是,不会将pybind11源代码显式地放到项目目录中,而是演示如何在配置时使用`FetchContent` (https://cmake.org/cmake/help/v3.11/module/FetchContent.html )。

为了在下一个示例中更好地重用代码,我们还将把所有源代码放到子目录中,并使用下面的项目布局:

```shell
.
├── account
├── account.cpp
├── account.hpp
├── CMakeLists.txt
└── test.py
├── account.cpp
├── account.hpp
├── CMakeLists.txt
└── test.py
└── CMakeLists.txt
```

## 具体实施

让我们详细分析一下这个项目中,各个CMakeLists.txt文件的内容:
让我们详细分析一下这个项目中,各个`CMakeLists.txt`文件的内容:

1. 主CMakeLists.txt文件:
1. `CMakeLists.txt`文件:

```cmake
# define minimum cmake version
Expand Down Expand Up @@ -183,7 +183,7 @@ add_subdirectory(
与之前的示例有两个不同之处:

* 不需要在系统上安装pybind11
* `${pybind11_sources_SOURCE_DIR}`子目录,包含pybind11的CMakelist.txt中,在我们开始构建项目时,这个目录并不存在
* `${pybind11_sources_SOURCE_DIR}`子目录,包含pybind11的`CMakelist.txt`,在我们开始构建项目时,这个目录并不存在

这个挑战的解决方案是用`FetchContent`,在配置时获取pybind11源代码和CMake模块,以便可以使用`add_subdirectory`引用。使用`FetchContent`模式,可以假设pybind11在构建树中可用,并允许构建和链接Python模块:

Expand All @@ -209,7 +209,7 @@ set_target_properties(account
)
```

主CMakeLists.txt文件的其余部分,都在执行测试(与前一个示例使用相同的`test.py`)。
`CMakeLists.txt`文件的其余部分,都在执行测试(与前一个示例使用相同的`test.py`)。

## 更多信息

Expand Down
26 changes: 13 additions & 13 deletions content/chapter9/9.6-chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,22 +192,22 @@ __all__ = [
```shell
.
├── account
├── account.h
├── CMakeLists.txt
├── implementation
├── c_cpp_interface.cpp
├── cpp_implementation.cpp
└── cpp_implementation.hpp
├── __init__.py
└── test.py
├── account.h
├── CMakeLists.txt
├── implementation
├── c_cpp_interface.cpp
├── cpp_implementation.cpp
└── cpp_implementation.hpp
├── __init__.py
└── test.py
└── CMakeLists.txt
```

## 具体实施

现在使用CMake来组合这些文件,形成一个Python模块:

1. 主CMakeLists.txt文件包含一个头文件。此外,根据GNU标准,设置编译库的位置:
1. `CMakeLists.txt`文件包含一个头文件。此外,根据GNU标准,设置编译库的位置:

```cmake
# define minimum cmake version
Expand All @@ -234,7 +234,7 @@ __all__ = [
add_subdirectory(account)
```

3. 主CMakeLists.txt文件以测试定义(需要Python解释器)结束:
3. `CMakeLists.txt`文件以测试定义(需要Python解释器)结束:

```cmake
# turn on testing
Expand Down Expand Up @@ -296,7 +296,7 @@ __all__ = [

## 工作原理

虽然,之前的示例要求我们显式地声明Python-C接口,并将Python名称映射到C(++)符号,但Python CFFI从C头文件(示例中是`account.h`)推断出这种映射。我们只需要向Python CFFI层提供描述C接口的头文件和包含符号的动态库。在主CMakeLists.txt文件中使用了环境变量集来实现这一点,这些环境变量可以在`__init__.py`中找到:
虽然,之前的示例要求我们显式地声明Python-C接口,并将Python名称映射到C(++)符号,但Python CFFI从C头文件(示例中是`account.h`)推断出这种映射。我们只需要向Python CFFI层提供描述C接口的头文件和包含符号的动态库。在主`CMakeLists.txt`文件中使用了环境变量集来实现这一点,这些环境变量可以在`__init__.py`中找到:

```python
# ...
Expand Down Expand Up @@ -332,7 +332,7 @@ else:

`get_lib_handle`函数打开头文件(使用`ffi.cdef `)并解析加载库(使用` ffi.dlopen`)。并返回库对象。前面的文件是通用的,可以在不进行修改的情况下重用,用于与Python和C或使用Python CFFI的其他语言进行接口的其他项目。

`_lib`库对象可以直接导出,这里做了一个额外的步骤,使Python接口在使用时,感觉更像Python:
`_lib`库对象可以直接导出,这里有一个额外的步骤,使Python接口在使用时,感觉更像Python:

```python
# we change names to obtain a more pythonic API
Expand Down Expand Up @@ -379,7 +379,7 @@ account.withdraw(account1, 5.0)
account.deposit(account2, 5.0)
```

为了导入`account`的Python模块,我们需要提供`ACCOUNT_HEADER_FILE``ACCOUNT_LIBRARY_FILE`环境变量,就像测试中那样:
为了导入`account`的Python模块,需要提供`ACCOUNT_HEADER_FILE``ACCOUNT_LIBRARY_FILE`环境变量,就像测试中那样:

```cmake
add_test(
Expand Down

0 comments on commit 1d55f3d

Please sign in to comment.