diff --git a/content/chapter9/9.0-chinese.md b/content/chapter9/9.0-chinese.md index 3eb2da6..0d27707 100644 --- a/content/chapter9/9.0-chinese.md +++ b/content/chapter9/9.0-chinese.md @@ -9,11 +9,11 @@ * 使用pybind11构建C++和Python项目 * 使用Python CFFI混合C,C++,Fortran和Python -有很多的库比较适合特定领域的任务。我们的库直接使用这些专业库,是一中快捷的方式,这样就可以使用来自其他专家组的多年经验进行开发。随着计算机体系结构和编译器的发展,编程语言也在不断发展。几年前,大多数科学软件都是用Fortran语言编写的,而现在,C、C++和解释语言——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 )。 -本章中,我们将展示如何以一种可移植且跨平台的方式集成用不同编译(C、C++和Fortran)和解释语言(Python)编写的代码。我们将展示如何利用CMake和一些工具集成不同编程语言。 \ No newline at end of file +本章中,我们将展示如何以一种可移植且跨平台的方式集成用不同编译(C/C++和Fortran)和解释语言(Python)编写的代码。我们将展示如何利用CMake和一些工具集成不同编程语言。 \ No newline at end of file diff --git a/content/chapter9/9.1-chinese.md b/content/chapter9/9.1-chinese.md index 7329ee9..9c0ffb4 100644 --- a/content/chapter9/9.1-chinese.md +++ b/content/chapter9/9.1-chinese.md @@ -6,7 +6,7 @@ Fortran作为高性能计算语言有着悠久的历史。目前,许多线性 ## 准备工作 -第7章中,我们把项目结构列为一个树。每个子目录都有一个CMakeLists.txt文件,其中包含与该目录相关的指令。这使我们可以对子目录进行限制中,如这个例子: +第7章中,我们把项目结构列为一个树。每个子目录都有一个`CMakeLists.txt`文件,其中包含与该目录相关的指令。这使我们可以对子目录进行限制中,如这个例子: ```shell . @@ -63,7 +63,7 @@ end module ## 具体实施 -我们有4个CMakeLists.txt实例要查看——根目录下1个,子目录下3个。让我们从根目录的CMakeLists.txt开始: +我们有4个`CMakeLists.txt`实例要查看——根目录下1个,子目录下3个。让我们从根目录的`CMakeLists.txt`开始: 1. 声明一个Fortran和C的混合语言项目: @@ -82,7 +82,7 @@ end module ${CMAKE_CURRENT_BINARY_DIR}/modules) ``` -3. 接下来,我们进入第一个子CMakeLists.txt,添加`src`子目录: +3. 接下来,我们进入第一个子`CMakeLists.txt`,添加`src`子目录: ```cmake add_subdirectory(src) @@ -132,7 +132,7 @@ end module ) ``` -`utils`子目录中,还有一个CMakeLists.txt,其只有一单行程序:我们创建一个新的库目标,子目录中的源文件将被编译到这个目标库中。并与这个目标没有依赖关系: +`utils`子目录中,还有一个`CMakeLists.txt`,其只有一单行程序:我们创建一个新的库目标,子目录中的源文件将被编译到这个目标库中。并与这个目标没有依赖关系: ```cmake add_library(utils SHARED util_strings.f90) @@ -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 @@ -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++`编译器。我们只列出了C、C++和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 diff --git a/content/chapter9/9.2-chinese.md b/content/chapter9/9.2-chinese.md index 0d53a9b..a902f9b 100644 --- a/content/chapter9/9.2-chinese.md +++ b/content/chapter9/9.2-chinese.md @@ -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++标准: diff --git a/content/chapter9/9.3-chinese.md b/content/chapter9/9.3-chinese.md index 0a45d76..f2ae73d 100644 --- a/content/chapter9/9.3-chinese.md +++ b/content/chapter9/9.3-chinese.md @@ -38,7 +38,7 @@ private: 使用这个示例代码,我们可以创建余额为零的银行帐户。可以在帐户上存款和取款,还可以使用`get_balance()`查询帐户余额。余额本身是`Account`类的私有成员。 -我们的目标是能够直接从Python与这个C++类进行交互——换句话说,在Python方面,我们希望能够做到这一点: +我们的目标是能够直接从Python与这个C++类进行交互。换句话说,在Python方面,我们希望能够做到这一点: ```python account = Account() @@ -49,7 +49,7 @@ account.withdraw(50.0) balance = account.get_balance() ``` -为此,需要一个Cython接口文件(我们将调用这个文件`account.pyx`): +为此,需要一个Cython接口文件(调用`account.pyx`): ```python # describe the c++ interface @@ -79,7 +79,7 @@ cdef class pyAccount: 如何生成Python接口: -1. CMakeLists.txt定义CMake依赖项、项目名称和语言: +1. `CMakeLists.txt`定义CMake依赖项、项目名称和语言: ```cmake # define minimum cmake version @@ -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 diff --git a/content/chapter9/9.4-chinese.md b/content/chapter9/9.4-chinese.md index 7ac1b07..0f57d9f 100644 --- a/content/chapter9/9.4-chinese.md +++ b/content/chapter9/9.4-chinese.md @@ -6,7 +6,7 @@ Boost库为C++代码提供了Python接口。本示例将展示如何在依赖于 ## 准备工作 -在保持`account.cpp`不变的同时,修改前一个示例中的接口文件(`account.hpp`): +保持`account.cpp`不变的同时,修改前一个示例中的接口文件(`account.hpp`): ```c++ #pragma once @@ -40,7 +40,7 @@ BOOST_PYTHON_MODULE(account) ## 具体实施 -以下,是如何在C++项目中使用Boost.Python的步骤: +如何在C++项目中使用Boost.Python的步骤: 1. 和之前一样,首先定义最低版本、项目名称、支持语言和默认构建类型: @@ -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 @@ -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等等。我们从特定的名称搜索到更通用的名称,已经解决了这个问题,只有在没有找到匹配的名称时才会失败: @@ -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)。后一项操作是通过设置适当的目标属性来执行的,如下所示: diff --git a/content/chapter9/9.5-chinese.md b/content/chapter9/9.5-chinese.md index 736caf3..fd02b48 100644 --- a/content/chapter9/9.5-chinese.md +++ b/content/chapter9/9.5-chinese.md @@ -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 @@ -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模块: @@ -209,7 +209,7 @@ set_target_properties(account ) ``` -主CMakeLists.txt文件的其余部分,都在执行测试(与前一个示例使用相同的`test.py`)。 +主`CMakeLists.txt`文件的其余部分,都在执行测试(与前一个示例使用相同的`test.py`)。 ## 更多信息 diff --git a/content/chapter9/9.6-chinese.md b/content/chapter9/9.6-chinese.md index 9c4d54d..927a18a 100644 --- a/content/chapter9/9.6-chinese.md +++ b/content/chapter9/9.6-chinese.md @@ -192,14 +192,14 @@ __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 ``` @@ -207,7 +207,7 @@ __all__ = [ 现在使用CMake来组合这些文件,形成一个Python模块: -1. 主CMakeLists.txt文件包含一个头文件。此外,根据GNU标准,设置编译库的位置: +1. 主`CMakeLists.txt`文件包含一个头文件。此外,根据GNU标准,设置编译库的位置: ```cmake # define minimum cmake version @@ -234,7 +234,7 @@ __all__ = [ add_subdirectory(account) ``` -3. 主CMakeLists.txt文件以测试定义(需要Python解释器)结束: +3. 主`CMakeLists.txt`文件以测试定义(需要Python解释器)结束: ```cmake # turn on testing @@ -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 # ... @@ -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 @@ -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(