实时 C++ 编程:高级特性与稳健环境搭建
1. 正则表达式与类型特性库
正则表达式是模板化的,例如
std::regex
实际上是
std::basic_regex<char>
的类型定义,因此正则表达式可用于字符串或其他类型的序列。匹配结果也是模板化的,并支持迭代器,便于使用 STL 标准算法进行操作。
<type_traits>
库可用于在编译时获取或修改对象类型的信息,它支持众多有用的模板,如
is_arithmetic
、
is_integral
、
is_floating_point
等。以下是一个查询整数类型特性的代码示例:
// appendix0a_15-001_type_traits.cpp
#include <type_traits>
using integral_type = int;
// Query the characteristics of integral_type.
constexpr bool type_is_arithmetic =
std::is_arithmetic<integral_type>::value;
constexpr bool type_is_integral =
std::is_integral<integral_type>::value;
constexpr bool type_is_signed =
std::is_signed<integral_type>::value;
此外,
<type_traits>
库中的模板还可用于添加或移除
const
和
volatile
等限定符。例如,使用
std::remove_volatile
模板移除
volatile
限定符:
// appendix0a_15-002_type_traits.cpp
#include <type_traits>
void do_something()
{
// The variable pv is volatile.
volatile int* pv = nullptr;
// Remove the volatile qualifier.
auto pu = std::remove_volatile<int*>::type(pv);
// Verify that the type of pu is not volatile.
// In other words, volatile has been removed.
constexpr bool volatile_has_been_removed =
(std::is_volatile<decltype(pu)>::value == false);
}
还可以使用
std::enable_if
在编译时启用模板代码序列,如类类型、构造函数和函数的声明。以下是一个
integer_wrap
类的示例:
// appendix0a_15-003_type_traits.cpp
class integer_wrap
{
public:
integer_wrap(const long long val = 0LL)
: my_value(val) { }
long long get_value() const
{
return my_value;
}
private:
long long my_value;
};
为了方便从各种内置整数类型构造
integer_wrap
类,可以使用
std::enable_if
模板:
// appendix0a_15-004_type_traits.cpp
#include <type_traits>
class integer_wrap
{
public:
template
<typename integral_type,
typename std::enable_if
<std::is_integral<integral_type>::value>::type* =
nullptr>
integer_wrap(const integral_type val = 0)
: my_value(static_cast<long long>(val)) { }
long long get_value() const
{
return my_value;
}
private:
long long my_value;
};
这样,新的构造函数可以为所有内置整数类型实例化,例如:
// appendix0a_15-004_type_traits.cpp
class integer_wrap
{
// ...
};
integer_wrap a(char('a'));
integer_wrap u(std::uint16_t(0x5555U));
integer_wrap n(1234);
2. 使用
std::any
和
std::variant
C++17 引入的
std::any
是一个非常有用的模板对象,可用于存储内置类型和任何类型的对象。以下是创建
std::any
实例的示例:
// appendix0a_16-001_any_basics.cpp
#include <any>
#include <string>
// Create from int.
std::any ai(42);
// Create from float.
std::any af(1.23F);
// Create from std::string.
std::any as(std::string("hello"));
可以使用
std::any_cast
模板函数从
std::any
中检索值:
// appendix0a_16-001_any_basics.cpp
#include <any>
#include <string>
// Create from int.
std::any ai(42);
// Create from float.
std::any af(1.23F);
// Create from std::string.
std::any as(std::string("hello"));
// Retrieve int value.
int i = std::any_cast<int>(ai);
// Retrieve float value.
float f = std::any_cast<float>(af);
// Retrieve std::string value.
std::string s = std::any_cast<std::string>(as);
还可以通过赋值运算符将一个
std::any
转换为另一个
std::any
:
// appendix0a_16-002_any_basics.cpp
#include <any>
// Create from int.
std::any ai(42);
// Create from float.
std::any af(1.23F);
void do_something()
{
// Assign an std::any holding int
// to an std::any holding float.
ai = af;
}
将多个
std::any
实例存储在 STL 容器中可以方便地进行操作,例如:
// appendix0a_16-003_any_and_stl_container.cpp
#include <any>
#include <algorithm>
#include <array>
#include <string>
void do_something()
{
// Fill an array with three instances of any.
const std::array<std::any, 3U> many
{{
std::any(42),
// int
std::any(1.23F),
// float
std::any(std::string("hello")) // std::string
}};
// Use random access on the container elements.
int n = std::any_cast<int>(many[0U]);
float f = std::any_cast<float>(many[1U]);
std::string str =
std::any_cast<std::string>(many[2U]);
// Query the container size.
const std::size_t size = many.size();
// Iterate in an algorithm
// and use a member function.
const bool has_many_values =
std::all_of(many.cbegin(),
many.cend(),
[](const std::any& a) -> bool
{
return a.has_value();
});
}
3. 结构化绑定声明
C++17 引入了结构化绑定声明,可用于创建引用现有对象或变量的标识符。例如:
// appendix0a_17-001_structured_binding.cpp
// my_array is initialized to {0,1,2}.
unsigned int my_array[3U] = { 0U, 1U, 2U };
auto& [x0, x1, x2] = my_array;
// x0 is 0 and refers to my_array[0U].
// x1 is 1 and refers to my_array[1U].
// x2 is 2 and refers to my_array[2U].
void do_something()
{
x0 += 5U;
x1 += 5U;
x2 += 5U;
// x0, x1 and x2 have been incremented by 5.
// This means that my_array is now {5,6,7}.
}
结构化绑定声明也可用于访问和操作元组中的元素。
4. 三路比较
C++20 引入的三路比较运算符(
<=>
)可用于比较两个内置类型或对象。其结果可以是小于、等于或大于零。例如:
#include <compare>
extern std::uint_fast8_t a;
extern std::uint_fast8_t b;
auto result = a <=> b;
这里,
result
是一个有符号值,如果
a < b
,则
result
小于零;如果
a == b
,则
result
等于零;如果
a > b
,则
result
大于零。
5. 应对实时 C++ 编程挑战
实时 C++ 编程面临性能、大小和安全等方面的严格约束,开发环境的稳定性对软件质量至关重要。以下是应对实时 C++ 编程挑战的一些建议:
-
选择合适的微控制器
:考虑性能和成本,根据应用需求选择 8 位、32 位或 64 位微控制器,并估算所需的程序内存和 RAM。如果有未来版本的计划,可选择可扩展的微控制器家族。
-
使微控制器原型运行起来
:学生和爱好者可使用商业微控制器入门套件,生产环境中应尽早确保有可用的原型板。
-
获取高质量的 C++ 编译器
:GCC 免费且符合语言标准,专业编译器可能在性能上更优,但价格较高。可进行市场调研和编译器基准测试来评估。
-
确保开发工具可用
:根据项目需求,确保微控制器编程器、模拟器、调试器等工具可用,并了解其使用方法。
-
设计和使用软件架构
:采用分层软件架构,将应用层与底层硬件特定代码分离,提高软件的可移植性。
-
建立编码能力
:通过独立学习和与社区交流,不断提高 C++ 编程能力。
-
遵循编码准则
:遵循既定的编码准则,可提高软件的可靠性。对于高可靠性项目,可使用静态语法检查器。
-
构建可重用代码库
:随着时间的推移,积累可重用的代码组件,如寄存器操作机制、自定义分配器、定时器等。
6. 软件架构设计
软件架构对软件质量至关重要,应根据软件规模设计合适的架构。以下是设计软件架构的步骤:
-
绘制简单的块图
:开始时,绘制主要软件层和组件的简单块图,可手绘。
-
创建目录和文件
:根据架构图创建相应的目录和文件,并填充初步的命名空间、类和函数。
-
完善实现细节
:开始时,类和函数可以是不完整的骨架,后续再添加实现细节。
-
确保命名关联
:确保命名空间、类、函数等的名称与架构图有明显的关联。
一个典型的微控制器软件架构由三个层次组成,具有逐渐提高的抽象级别:
| 层次 | 描述 |
| ---- | ---- |
| 微控制器抽象层(MCAL) | 包含微控制器特定的外设驱动,如定时器、看门狗或通信接口。 |
| 系统层 | 实现中间系统级软件,如启动例程和监控功能。 |
| 应用层 | 包含高级应用程序软件,模块应保持完全可移植。 |
操作系统和项目实用程序可由所有三个层次使用。
7. 建立和遵守运行时限制
微控制器编程对时间敏感,软件的不可预测时序可能导致问题。为解决此问题,可通过以下步骤建立和遵守运行时限制:
-
确定任务和中断的优先级类
:识别系统中任务和中断的优先级类。
-
定义运行时约束
:为每个优先级类定义运行时约束,包括设计目标和最坏情况下的最大限制。
以下是一个具有三个优先级类的系统的运行时限制示例:
| 优先级类 | 设计目标 [µs] | 最坏情况最大 [µs] |
| ---- | ---- | ---- |
| 高优先级中断 | <10 | ≲25 |
| 低优先级中断 | <40 | ≲100 |
| 所有任务 | <500 | ≲1000 |
在设计嵌入式微控制器系统时,应提前识别最耗时的软件过程,并设计时间粒度以实现工作负载的均匀分布。避免使用长时间占用 CPU 的阻塞调用。
实时 C++ 编程:高级特性与稳健环境搭建(续)
8. 正则表达式使用流程总结
正则表达式在实时 C++ 编程中有重要应用,下面通过 mermaid 流程图展示其使用的一般流程:
graph TD;
A[定义正则表达式] --> B[准备待匹配字符串];
B --> C[进行匹配操作];
C --> D{匹配是否成功};
D -- 是 --> E[处理匹配结果];
D -- 否 --> F[输出匹配失败信息];
在实际使用中,如前面所述,匹配结果是模板化的,并且支持迭代器,方便使用 STL 标准算法进行操作。例如,对于正则表达式匹配操作,我们可以按照以下步骤进行:
1. 定义正则表达式,可选用
std::regex
类型。
2. 准备好要进行匹配的整个字符串。
3. 使用相应的匹配函数进行匹配操作。
4. 根据匹配结果进行处理,如果匹配成功,可进一步操作匹配结果;若失败,则给出相应提示。
9. 类型特性库应用拓展
<type_traits>
库的应用十分广泛,除了前面提到的查询类型特性、移除限定符和启用模板代码序列外,还可以在模板元编程中发挥重要作用。下面通过一个简单的示例展示如何判断两个类型是否相同:
#include <type_traits>
#include <iostream>
template<typename T, typename U>
void check_types() {
if (std::is_same<T, U>::value) {
std::cout << "Types are the same." << std::endl;
} else {
std::cout << "Types are different." << std::endl;
}
}
int main() {
check_types<int, int>();
check_types<int, float>();
return 0;
}
在这个示例中,我们定义了一个模板函数
check_types
,使用
std::is_same
模板来判断两个类型是否相同,并输出相应的信息。
10.
std::any
和
std::variant
的更多使用场景
std::any
和
std::variant
在处理不同类型的数据时非常有用。除了前面介绍的基本使用方法,它们还可以用于实现一些复杂的数据结构和算法。例如,我们可以使用
std::any
实现一个简单的异构容器:
#include <any>
#include <vector>
#include <iostream>
void print_any(const std::any& a) {
if (a.type() == typeid(int)) {
std::cout << std::any_cast<int>(a) << std::endl;
} else if (a.type() == typeid(float)) {
std::cout << std::any_cast<float>(a) << std::endl;
} else if (a.type() == typeid(std::string)) {
std::cout << std::any_cast<std::string>(a) << std::endl;
}
}
int main() {
std::vector<std::any> heterogeneous_container;
heterogeneous_container.push_back(42);
heterogeneous_container.push_back(3.14F);
heterogeneous_container.push_back(std::string("hello"));
for (const auto& element : heterogeneous_container) {
print_any(element);
}
return 0;
}
在这个示例中,我们创建了一个
std::vector<std::any>
类型的异构容器,向其中添加了不同类型的元素,并通过
print_any
函数打印出每个元素的值。
11. 结构化绑定声明的优势与应用场景
结构化绑定声明在处理数组、元组等数据结构时非常方便,它可以提高代码的可读性和简洁性。下面通过一个处理元组的示例进一步说明:
#include <iostream>
#include <tuple>
std::tuple<int, float, std::string> get_data() {
return std::make_tuple(10, 3.14F, "world");
}
int main() {
auto [i, f, s] = get_data();
std::cout << "Integer: " << i << std::endl;
std::cout << "Float: " << f << std::endl;
std::cout << "String: " << s << std::endl;
return 0;
}
在这个示例中,我们定义了一个函数
get_data
,返回一个包含整数、浮点数和字符串的元组。在
main
函数中,使用结构化绑定声明将元组中的元素分别绑定到变量
i
、
f
和
s
上,然后打印出这些变量的值。
12. 三路比较在排序算法中的应用
三路比较运算符(
<=>
)在排序算法中可以简化代码的实现。例如,我们可以使用它来实现一个简单的冒泡排序算法:
#include <iostream>
#include <vector>
#include <compare>
template<typename T>
void bubble_sort(std::vector<T>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
if (arr[j] <=> arr[j + 1] > 0) {
std::swap(arr[j], arr[j + 1]);
}
}
}
}
int main() {
std::vector<int> numbers = {5, 3, 8, 1, 2};
bubble_sort(numbers);
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在这个示例中,我们使用三路比较运算符(
<=>
)来比较数组中的元素,并根据比较结果进行交换,从而实现冒泡排序。
13. 实时 C++ 编程的综合实践
在实际的实时 C++ 编程中,我们需要综合运用前面介绍的各种知识和技巧。例如,我们可以设计一个简单的嵌入式系统,包含多个任务和中断处理。以下是一个简化的示例:
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
// 模拟高优先级中断处理函数
void high_priority_interrupt() {
std::cout << "High priority interrupt handling..." << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(5));
}
// 模拟低优先级中断处理函数
void low_priority_interrupt() {
std::cout << "Low priority interrupt handling..." << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(20));
}
// 模拟任务处理函数
void task() {
std::cout << "Task handling..." << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(200));
}
int main() {
// 模拟高优先级中断
std::thread high_thread(high_priority_interrupt);
high_thread.join();
// 模拟低优先级中断
std::thread low_thread(low_priority_interrupt);
low_thread.join();
// 模拟任务
std::thread task_thread(task);
task_thread.join();
return 0;
}
在这个示例中,我们模拟了高优先级中断、低优先级中断和任务的处理过程,并使用
std::thread
来模拟并发执行。同时,我们可以根据前面介绍的运行时限制原则,对这些处理过程的时间进行控制,确保系统的稳定性和可靠性。
通过以上内容,我们对实时 C++ 编程的高级特性和稳健环境搭建有了更深入的了解。在实际应用中,我们需要根据具体的需求和场景,灵活运用这些知识和技巧,以开发出高质量的实时 C++ 软件。
超级会员免费看
1549

被折叠的 条评论
为什么被折叠?



