docs: provide CN translation for api-guides/cplusplus.rst

This commit is contained in:
caixinying-git 2023-06-14 18:40:53 +08:00
parent eb8883cc20
commit 7c560c61c3
2 changed files with 232 additions and 29 deletions

View File

@ -1,6 +1,8 @@
C++ Support
===========
:link_to_translation:`zh_CN:[中文]`
.. highlight:: cpp
ESP-IDF is primarily written in C and provides C APIs. However, ESP-IDF supports development of applications in C++. This document covers various topics relevant to C++ development.
@ -11,19 +13,19 @@ The following C++ features are supported:
- :ref:`cplusplus_multithreading`
- :ref:`cplusplus_rtti`
- :doc:`thread-local-storage` (``thread_local`` keyword)
- All C++ features implemented by GCC, except for some :ref:`limitations <cplusplus_limitations>`. See `GCC documentation <https://gcc.gnu.org/projects/cxx-status.html>`_ for details on features implemented by GCC.
- All C++ features implemented by GCC, except for some :ref:`cplusplus_limitations`. See `GCC documentation <https://gcc.gnu.org/projects/cxx-status.html>`_ for details on features implemented by GCC.
esp-idf-cxx Component
---------------------
``esp-idf-cxx`` Component
-------------------------
`esp-idf-cxx <https://github.com/espressif/esp-idf-cxx>`_ component provides higher-level C++ APIs for some of the ESP-IDF features. This component is available from the `IDF Component Registry <https://components.espressif.com/components/espressif/esp-idf-cxx>`_.
`esp-idf-cxx <https://github.com/espressif/esp-idf-cxx>`_ component provides higher-level C++ APIs for some of the ESP-IDF features. This component is available from the `ESP-IDF Component Registry <https://components.espressif.com/components/espressif/esp-idf-cxx>`_.
C++ language standard
C++ Language Standard
---------------------
By default, ESP-IDF compiles C++ code with C++23 language standard with GNU extensions (``-std=gnu++23``).
To compile the source code of a certain component using a different language standard, set the desired compiler flag in the component CMakeLists.txt file:
To compile the source code of a certain component using a different language standard, set the desired compiler flag in the component's ``CMakeLists.txt`` file:
.. code-block:: cmake
@ -43,36 +45,45 @@ See :example:`cxx/pthread` for an example of creating threads in C++.
.. note::
The destructor of `std::jthread <https://en.cppreference.com/w/cpp/thread/jthread>`_ can only safely be called from a task that has been created by :ref:`the IDF implementation of pthread_create() <posix_thread_api>` or by the `C++ threading library API <https://en.cppreference.com/w/cpp/thread>`_.
The destructor of `std::jthread <https://en.cppreference.com/w/cpp/thread/jthread>`_ can only safely be called from a task that has been created by :ref:`posix_thread_api` or by the `C++ threading library API <https://en.cppreference.com/w/cpp/thread>`_.
.. _cplusplus_exceptions:
Exception handling
Exception Handling
------------------
Support for C++ Exceptions in ESP-IDF is disabled by default, but can be enabled using the :ref:`CONFIG_COMPILER_CXX_EXCEPTIONS` option.
If an exception is thrown, but there is no ``catch`` block, the program will be terminated by the ``abort`` function, and the backtrace will be printed. See :doc:`Fatal Errors <fatal-errors>` for more information about backtraces.
If an exception is thrown, but there is no ``catch`` block, the program is terminated by the ``abort`` function, and the backtrace is printed. See :doc:`fatal-errors` for more information about backtraces.
C++ Exceptions should *only* be used for exceptional cases, something happening unexpectedly and that is quite rare, e.g. an event that happens less frequently than 1 every 100 times. *Do not* use them for control flow (see also the section about resource usage below)! For more information on how to use C++ Exceptions, see the `ISO C++ FAQ <https://isocpp.org/wiki/faq/exceptions>`_ and `CPP Core Guidelines <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-errors>`_.
C++ Exceptions should **only** be used for exceptional cases, i.e., something happening unexpectedly and occurs rarely, such as events that happen less frequently than 1/100 times. **Do not** use them for control flow (see also the section about resource usage below). For more information on how to use C++ Exceptions, see the `ISO C++ FAQ <https://isocpp.org/wiki/faq/exceptions>`_ and `CPP Core Guidelines <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-errors>`_.
See :example:`cxx/exceptions` for an example of C++ exception handling.
C++ Exception Handling and Resource Usage
C++ Exception Handling and Resource Usage
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Enabling exception handling normally increases application binary size by a few KB.
Additionally, it may be necessary to reserve some amount of RAM for exception emergency pool. Memory from this pool will be used if it is not possible to allocate exception object from the heap. The amount of memory in the emergency pool can be set using the :ref:`CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE` variable. Some additional stack memory (around 200 bytes) will also be used if and only if a C++ Exception is actually thrown, because it requires calling some functions from the top of the stack to initiate exception handling.
Additionally, it may be necessary to reserve some amount of RAM for the exception emergency memory pool. Memory from this pool is used if it is not possible to allocate an exception object from the heap.
The run time of code using C++ exceptions depends on what actually happens at run time. If no exception is thrown, the code tends to be somewhat faster since there is no need to check error codes. If an exception is thrown, the run time of the code that handles exceptions will be orders of magnitude slower than code returning an error code. This increase may or may not be significant, however, in the entire application, in particular if the error handling requires additional action, such as a user input or messaging to a cloud. But exception-throwing code should never be used in real-time critical code paths.
The amount of memory in the emergency pool can be set using the :ref:`CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE` variable.
Some additional stack memory (around 200 bytes) is also used if and only if a C++ Exception is actually thrown, because it requires calling some functions from the top of the stack to initiate exception handling.
The run time of code using C++ exceptions depends on what actually happens at run time.
- If no exception is thrown, the code tends to be somewhat faster since there is no need to check error codes.
- If an exception is thrown, the run time of the code that handles exceptions is orders of magnitude slower than code returning an error code.
If an exception is thrown, the run time of the code that unwinds the stack is orders of magnitude slower than code returning an error code. The significance of the increased run time will depend on the application's requirements and implementation of error handling (e.g., requiring user input or messaging to a cloud). As a result, exception-throwing code should never be used in real-time critical code paths.
.. _cplusplus_rtti:
Runtime Type Information (RTTI)
-------------------------------
Support for RTTI is disabled by default, but can be enabled using :ref:`CONFIG_COMPILER_CXX_RTTI` option.
Support for RTTI in ESP-IDF is disabled by default, but can be enabled using :ref:`CONFIG_COMPILER_CXX_RTTI` option.
Enabling this option compiles all C++ files with RTTI support enabled, which allows using ``dynamic_cast`` conversion and ``typeid`` operator. Enabling this option typically increases the binary size by tens of kB.
@ -84,14 +95,16 @@ Developing in C++
The following sections provide tips on developing ESP-IDF applications in C++.
Combining C and C++ code
Combining C and C++ Code
^^^^^^^^^^^^^^^^^^^^^^^^
When part of the application is developed in C and part in C++, it is important to understand the concept of `language linkage <https://en.cppreference.com/w/cpp/language/language_linkage>`_.
When an application is developed using both C and C++, it is important to understand the concept of `language linkage <https://en.cppreference.com/w/cpp/language/language_linkage>`_.
In order for a C++ function to be callable from C code, it has to be both *declared* and *defined* with C linkage (``extern "C"``)::
In order for a C++ function to be callable from C code, it has to be both **declared** and **defined** with C linkage (``extern "C"``):
// declaration in the header file:
.. code-block:: cpp
// declaration in the .h file:
#ifdef __cplusplus
extern "C" {
#endif
@ -108,9 +121,11 @@ In order for a C++ function to be callable from C code, it has to be both *decla
}
In order for a C function to be callable from C++, it has to be *declared* with C linkage::
In order for a C function to be callable from C++, it has to be **declared** with C linkage:
// declaration in the header file:
.. code-block:: c
// declaration in .h file:
#ifdef __cplusplus
extern "C" {
#endif
@ -130,7 +145,9 @@ In order for a C function to be callable from C++, it has to be *declared* with
Defining ``app_main`` in C++
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ESP-IDF expects the application entry point, ``app_main``, to be defined with C linkage. When ``app_main`` is defined in a .cpp source file, it has to be designated as ``extern "C"``::
ESP-IDF expects the application entry point, ``app_main``, to be defined with C linkage. When ``app_main`` is defined in a .cpp source file, it has to be designated as ``extern "C"``:
.. code-block:: cpp
extern "C" void app_main()
{
@ -138,12 +155,12 @@ ESP-IDF expects the application entry point, ``app_main``, to be defined with C
.. _cplusplus_designated_initializers:
Designated initializers
Designated Initializers
^^^^^^^^^^^^^^^^^^^^^^^
Many of the ESP-IDF components use :ref:`configuration structures <api_reference_config_structures>` as arguments to the initialization functions. ESP-IDF examples written in C routinely use `designated initializers <https://en.cppreference.com/w/c/language/struct_initialization>`_ to fill these structures in a readable and a maintainable way.
Many of the ESP-IDF components use :ref:`api_reference_config_structures` as arguments to the initialization functions. ESP-IDF examples written in C routinely use `designated initializers <https://en.cppreference.com/w/c/language/struct_initialization>`_ to fill these structures in a readable and a maintainable way.
C and C++ languages have different rules with regards to the designated initializers. For example, C++ language version C++23, currently the default in ESP-IDF, does not support out-of-order designated initialization, nested designated initialization, mixing of designated initializers and regular initializers, and designated initialization of arrays. Therefore, when porting ESP-IDF C examples to C++, some changes to the structure initializers may be necessary. See the `C++ aggregate initialization reference <https://en.cppreference.com/w/cpp/language/aggregate_initialization>`_ for more details.
C and C++ languages have different rules with regards to the designated initializers. For example, C++23 (currently the default in ESP-IDF) does not support out-of-order designated initialization, nested designated initialization, mixing of designated initializers and regular initializers, and designated initialization of arrays. Therefore, when porting ESP-IDF C examples to C++, some changes to the structure initializers may be necessary. See the `C++ aggregate initialization reference <https://en.cppreference.com/w/cpp/language/aggregate_initialization>`_ for more details.
iostream
@ -151,7 +168,7 @@ iostream
``iostream`` functionality is supported in ESP-IDF, with a couple of caveats:
1. Normally ESP-IDF build process eliminates the unused code. However in the case of iostreams, simply including ``<iostream>`` header in one of the source files significantly increases the binary size (by about 200 kB).
1. Normally, ESP-IDF build process eliminates the unused code. However, in the case of iostreams, simply including ``<iostream>`` header in one of the source files significantly increases the binary size by about 200 kB.
2. By default, ESP-IDF uses a simple non-blocking implementation of the standard input stream (``stdin``). To get the usual behavior of ``std::cin``, the application has to initialize the UART driver and enable the blocking mode as shown in :example_file:`common_components/protocol_examples_common/stdin_out.c`.
.. _cplusplus_limitations:
@ -159,12 +176,12 @@ iostream
Limitations
-----------
- Linker script generator doesn't support function level placements for functions with C++ linkage.
- Linker script generator does not support function level placements for functions with C++ linkage.
- Various section attributes (such as ``IRAM_ATTR``) are ignored when used with template functions.
- Vtables are placed into Flash and are not accessible when the flash cache is disabled. Therefore, virtual function calls should be avoided in :ref:`IRAM-safe interrupt handlers <iram-safe-interrupt-handlers>`. Placement of Vtables cannot be adjusted using the linker script generator, yet.
- Vtables are placed into Flash and are not accessible when the flash cache is disabled. Therefore, virtual function calls should be avoided in :ref:`iram-safe-interrupt-handlers`. Placement of Vtables cannot be adjusted using the linker script generator, yet.
- C++ filesystem (``std::filesystem``) features are not supported.
What to Avoid
-------------
Do not use ``setjmp``/``longjmp`` in C++! ``longjmp`` blindly jumps up the stack without calling any destructors, easily introducing undefined behavior and memory leaks. Use C++ exceptions instead, they will guarantee correctly calling destructors. If you cannot use C++ exceptions, use alternatives (except ``setjmp``/``longjmp`` themselves) such as simple return codes.
Do not use ``setjmp``/``longjmp`` in C++. ``longjmp`` blindly jumps up the stack without calling any destructors, easily introducing undefined behavior and memory leaks. Use C++ exceptions instead, they guarantee correctly calling destructors. If you cannot use C++ exceptions, use alternatives (except ``setjmp``/``longjmp`` themselves) such as simple return codes.

View File

@ -1 +1,187 @@
.. include:: ../../en/api-guides/cplusplus.rst
C++ 支持
===========
:link_to_translation:`en:[English]`
.. highlight:: cpp
ESP-IDF 主要使用 C 语言编写,并提供 C 语言 API。但 ESP-IDF 也支持使用 C++ 开发应用程序,与 C++ 开发相关的各种主题在本文档中列出。
ESP-IDF 支持以下 C++ 功能:
- :ref:`cplusplus_exceptions`
- :ref:`cplusplus_multithreading`
- :ref:`cplusplus_rtti`
- :doc:`thread-local-storage` ``thread_local`` 关键字)
- 除部分 :ref:`cplusplus_limitations`,所有由 GCC 实现的 C++ 功能均受支持。有关由 GCC 所实现功能的详细信息,请参阅 `GCC 文档 <https://gcc.gnu.org/projects/cxx-status.html>`_
``esp-idf-cxx`` 组件
-------------------------
`esp-idf-cxx <https://github.com/espressif/esp-idf-cxx>`_ 组件为一些 ESP-IDF 中的功能提供了更高级别的 C++ API该组件可以从 `ESP-IDF 组件注册表 <https://components.espressif.com/components/espressif/esp-idf-cxx>`_ 中获取。
C++ 语言标准
---------------------
默认情况下ESP-IDF 使用 C++23 语言标准和 GNU 扩展 (``-std=gnu++23``) 编译 C++ 代码。
要使用其他语言标准编译特定组件的源代码,请按以下步骤,在组件的 CMakeLists.txt 文件中设置所需的编译器标志:
.. code-block:: cmake
idf_component_register( ... )
target_compile_options(${COMPONENT_LIB} PRIVATE -std=gnu++11)
如果组件的公共头文件也需要以该语言标准编译,请使用 ``PUBLIC`` 而非 ``PRIVATE``
.. _cplusplus_multithreading:
多线程
--------------
支持 C++ 线程互斥锁和条件变量。C++ 线程基于 pthread 构建,而 pthread 封装了 FreeRTOS 任务。
有关在 C++ 中创建线程的示例,请参阅 :example:`cxx/pthread`
.. note::
`std::jthread <https://en.cppreference.com/w/cpp/thread/jthread>`_ 的析构函数只能从 :ref:`posix_thread_api``C++ 线程库 API <https://en.cppreference.com/w/cpp/thread>`_ 创建的任务中安全地调用。
.. _cplusplus_exceptions:
异常处理
------------------
ESP-IDF 默认禁用对 C++ 异常处理的支持,可以用 :ref:`CONFIG_COMPILER_CXX_EXCEPTIONS` 选项启用该支持。
如果抛出了异常处理,却没有相应的 ``catch`` 块,程序将由 ``abort`` 函数终止,并打印回溯信息。有关回溯信息的更多信息,请参见 :doc:`fatal-errors`
C++ 异常处理应 **仅** 应用于异常情况,即意外情况及罕见情况,如发生频率低于 1% 的事件。**请勿** 将 C++ 异常处理用于流程控制,详情请参阅下文的资源使用部分。有关使用 C++ 异常处理的更多详情,请参阅 `ISO C++ FAQ <https://isocpp.org/wiki/faq/exceptions>`_`CPP 核心指南 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-errors>`_
有关 C++ 异常处理的示例,请参阅 :example:`cxx/exceptions`
C++ 异常处理及所需资源
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
启用异常处理后,应用程序的二进制文件通常会增加几个 KB。
此外,可能需要为异常处理应急内存池保留一部分 RAM。如果无法从堆内存中分配异常处理对象则会使用该池中的内存。
使用 :ref:`CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE` 变量可以设置异常处理应急内存池的内存量。
当且仅当 C++ 异常抛出时,会使用额外的栈内存(约 200 字节),从而从栈内存顶部调用函数,启动异常处理。
使用 C++ 异常处理的代码的运行时间取决于运行时实际发生的情况。
- 如果没有抛出异常,则异常处理的代码运行速度会更快,因为无需检查错误代码。
- 如果抛出异常,异常处理代码的运行时间会比返回错误代码的代码长几个数量级。
如果抛出异常,解开栈代码的速度要比返回错误代码慢好几个数量级。所增加的运行时长取决于应用程序的要求和错误处理的实现方式(例如,是否需要用户输入或发送消息到云端)。因此,在实时关键的代码路径中,不应使用会抛出异常的代码。
.. _cplusplus_rtti:
运行时类型信息 (RTTI)
-------------------------------
ESP-IDF 默认禁用对 RTTI 的支持,可以用 :ref:`CONFIG_COMPILER_CXX_RTTI` 选项启用该支持。
启用此选项,将以启用了 RTTI 支持的方式编译所有的 C++ 文件,并支持使用 ``dynamic_cast`` 转换和 ``typeid`` 运算符。启用此选项通常会增加几十 KB 的二进制文件大小。
有关在 ESP-IDF 中使用 RTTI 的示例,请参阅 :example:`cxx/rtti`
在 C++ 中进行开发
-----------------
以下部分提供了在 C++ 中开发 ESP-IDF 应用程序的一些技巧。
组合 C 和 C++ 代码
^^^^^^^^^^^^^^^^^^^^^^^^
当应用程序的不同部分使用 C 和 C++ 开发时,理解 `语言链接性 <https://en.cppreference.com/w/cpp/language/language_linkage>`_ 的概念非常重要。
为了能够从 C 代码中调用 C++ 函数,该 C++ 函数必须使用 C 链接 (``extern "C"``) 进行 **声明****定义**
.. code-block:: cpp
// 在 .h 文件中声明:
#ifdef __cplusplus
extern "C" {
#endif
void my_cpp_func(void);
#ifdef __cplusplus
}
#endif
// 在 .cpp 文件中进行定义:
extern "C" void my_cpp_func(void) {
// ...
}
为了能够从 C++ 中调用 C 函数,该 C 函数必须使用 C 链接 **声明**
.. code-block:: C
// 在 .h 文件中声明:
#ifdef __cplusplus
extern "C" {
#endif
void my_c_func(void);
#ifdef __cplusplus
}
#endif
// 在 .c 文件中进行定义:
void my_c_func(void) {
// ...
}
在 C++ 中定义 ``app_main``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ESP-IDF 希望应用程序入口点 ``app_main`` 以 C 链接定义。当 ``app_main`` 在 .cpp 源文件中定义时,必须以 ``extern "C"`` 标识:
.. code-block:: cpp
extern "C" void app_main()
{
}
.. _cplusplus_designated_initializers:
指定初始化器
^^^^^^^^^^^^^^^^^^^^^^^
许多 ESP-IDF 组件会以 :ref:`api_reference_config_structures` 作为初始化函数的参数。用 C 编写的 ESP-IDF 示例通常使用 `指定初始化器 <https://en.cppreference.com/w/c/language/struct_initialization>`_,以可读且可维护的方式填充有关结构体。
C 和 C++ 语言对于指定初始化器有不同的规则。例如C++23当前在 ESP-IDF 中默认使用)不支持无序指定初始化、嵌套指定初始化、混合使用指定初始化器和常规初始化器,而对数组进行指定初始化。因此,当将 ESP-IDF 的 C 示例移植到 C++ 时,可能需要对结构体初始化器进行一些更改。详细信息请参阅 `C++ aggregate initialization reference <https://en.cppreference.com/w/cpp/language/aggregate_initialization>`_
iostream
^^^^^^^^
ESP-IDF 支持 ``iostream`` 功能,但应注意:
1. ESP-IDF 在构建过程中通常会删除未使用的代码。然而,在使用 iostreams 的情况下,仅在其中一个源文件包含 ``<iostream>`` 头文件就会使二进制文件增加大约 200 kB。
2. ESP-IDF 默认使用简单的非阻塞机制来处理标准输入流 (``stdin``)。要获得 ``std::cin`` 的常规行为,应用程序必须初始化 UART 驱动程序,并启用阻塞模式,详情请参阅 :example_file:`common_components/protocol_examples_common/stdin_out.c`
.. _cplusplus_limitations:
限制
-----------
- 链接脚本生成器不支持将具有 C++ 链接的函数单独放置在内存的特定位置。
- 当与模板函数一起使用时,会忽略各种节属性(例如 ``IRAM_ATTR``)。
- vtable 位于 flash 中,在禁用 flash 缓存时无法访问。因此,在 :ref:`iram-safe-interrupt-handlers` 中应避免调用虚拟函数。目前尚无法使用链接器脚本生成器调整 vtable 的放置位置。
- 不支持 C++ 文件系统 (``std::filesystem``) 功能。
注意事项
-------------
请勿在 C++ 中使用 ``setjmp``/``longjmp````longjmp`` 会在不调用任何析构函数的情况下盲目跳出堆栈,容易引起未定义的行为和内存泄漏。请改用 C++ 异常处理,这类程序可以确保正确调用析构函数。如果无法使用 C++ 异常处理,请使用其他替代方案( ``setjmp``/``longjmp`` 除外),如简单的返回码。