gpt4 book ai didi

c++ - 使用 volatile 标准模板对象的方法

转载 作者:太空宇宙 更新时间:2023-11-04 15:11:36 24 4
gpt4 key购买 nike

我有一些代码,现在我希望在 Teensy 3.6 微 Controller 上的基于定时器的中断中运行。该代码访问一个类的[全局]对象数组。我已将该数组和所有成员变量标记为易变的,我认为这是正确处理中断的第一步。

我标记为 volatile 的成员变量之一是 std::bitset,我想称它为非 volatile 方法,但我不能这样做

"passing 'volatile std::bitset<16u>' as 'this' argument discards qualifiers [-fpermissive]"

我认为我可以复制 bitset 库并将所有内容切换为 volatile,但我认为这不是必需的,所以我认为要么有更好的解决方案,要么我的想法不正确。

请告诉我应该做什么。

这些答案似乎建议在 ISR 中访问全局变量时使用 volatile: C 'Volatile' keyword in ISR and multithreaded program? ,

Why is volatile needed in C? ,

What is the correct way of using C++ objects (and volatile) inside interrupt routines? ,

Is volatile needed when variable is only read during interrupt

volatile keyword usage in ISR function in micro-controller programing

这是许多推荐使用的外部来源的补充。也许我原来的信息不清楚,或者我的情况可能与这些不同。

最佳答案

您不应该将所有内容设置为易变的。 Volatile 有一个特定的目的,那就是防止编译器优化内存的读写。让我们看一个非常简单的例子。

int regular_sum(int* ptr) {
int a = *ptr;
int b = *ptr;
return a + b;
}
int volatile_sum(int volatile* ptr) {
int a = *ptr;
int b = *ptr;
return a + b;
}

当我们查看程序集时,我们会在 regular_sum 中看到,编译器意识到您正在两次取消引用同一个指针,并将其优化为仅一次取消引用。但是在volatile_sum ,编译器插入两个解引用:

regular_sum(int*):
mov eax, DWORD PTR [rdi]
add eax, eax
ret
volatile_sum(int volatile*):
mov eax, DWORD PTR [rdi]
mov edx, DWORD PTR [rdi]
add eax, edx
ret

优化很好,而且大多数时候,您不需要使用 volatile。如果您正在执行内存映射 IO,或者您正在将值写入引脚,就好像它们是指针一样,那是您使用 volatile 的地方。重申 Nathan Oliver 所说的话,

You only need to use volatile on variables where the hardware can change the value of the variable because the compiler can't know about that. This is what volatile is for, letting the compiler know this is a special variable that could be changed in a manner it doesn't know about. If the hardware cant change the value on you, then you don't need volatile.

但是如果您正在对一个对象进行计算,不要使用 volatile。对普通对象进行计算,然后将结果复制到您的 volatile 指针。

不稳定和中断服务例程。

volatile比较合适在可能被中断服务例程修改的全局变量上。也就是说,volatile不能与 std::bitset 之类的对象一起使用因为std::bitset不支持 volatile 操作,并且 std::bitset不可简单复制。

在这方面,您有两种选择:

  • 使用包含易 rebase 元的容器(例如 std::vector<volatile bool>
  • 编写自己的类,支持 volatile。

如果您有一个可简单复制的类,那么您可以执行如下操作。首先,我们必须定义函数以允许我们复制到可变类型和从可变类型复制:

template<class T>
T volatile_copy(T const volatile& source) {
static_assert(std::is_trivially_copyable_v<T>, "Input must be trivially copyable");
T dest;
auto* dest_ptr = dynamic_cast<char*>(&dest);
auto* source_ptr = dynamic_cast<char const volatile*>(&source);

for(int i = 0; i < sizeof(T); i++) {
dest_ptr[i] = source_ptr[i];
}

return dest;
}

template<class T>
void volatile_assign(T volatile& dest, T const& source) {
static_assert(std::is_trivially_copyable_v<T>, "Input must be trivially copyable");
auto* source_ptr = dynamic_cast<char*>(&source);
auto* dest_ptr = dynamic_cast<char volatile*>(&dest);

for(int i = 0; i < sizeof(T); i++) {
dest_ptr[i] = source_ptr[i];
}
}

然后,我们可以正常编写一个类,只要它是普通可复制的,我们就可以从可变版本创建一个拷贝:

struct MyBitset {
uint64_t bits;
// Logic

void flip() {
bits = ~bits;
}
void addOne() {
bits++;
}
};

volatile MyBitset flags;

void interrupt_handler() {
auto local = volatile_copy(flags);

// Do stuff to local

volatile_assign(flags, local);
};

我们也可以将这种行为封装在一个类中,这样我们就可以“ check out ”volatile变量:

template<class T>
struct Checkout {
T local;
T volatile& source;
Checkout(T volatile& source)
: local(volatile_copy(source))
, source(source) {}
void save() {
volatile_assign(source, local);
}
~Checkout() {
save();
}
};

使用它可以让我们创建 volatile 的本地拷贝变量,对其进行修改,结果将自动保存:

volatile MyBitset flags;

void interrupt_handler() {
auto f = Checkout(::flags);

f.local.flip(); //We can call whatever member functions we want on the local

// When the function exits, changes made to the local are automatically assigned to the volatile global
}

关于c++ - 使用 volatile 标准模板对象的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57499440/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com