C++ 使用Volatile标准模板对象的方法
我有一些代码,我现在希望运行在一个基于定时器的中断在一个小3.6微控制器。代码访问类的对象的[global]数组。我已经将该数组和所有成员变量标记为volatile,我认为这是正确处理中断的第一步 我标记为volatile的其中一个成员变量是std::bitset,我想调用它的非volatile方法,但我不能这样做C++ 使用Volatile标准模板对象的方法,c++,arduino,interrupt,volatile,c++-standard-library,C++,Arduino,Interrupt,Volatile,C++ Standard Library,我有一些代码,我现在希望运行在一个基于定时器的中断在一个小3.6微控制器。代码访问类的对象的[global]数组。我已经将该数组和所有成员变量标记为volatile,我认为这是正确处理中断的第一步 我标记为volatile的其中一个成员变量是std::bitset,我想调用它的非volatile方法,但我不能这样做 "passing 'volatile std::bitset<16u>' as 'this' argument discards qualifiers [-fpermis
"passing 'volatile std::bitset<16u>' as 'this' argument discards qualifiers [-fpermissive]"
“将'volatile std::bitset'作为'this'参数传递将丢弃限定符[-fppermissive]。”
我想我可以复制位集库并将所有内容切换到volatile,但我认为这不是必需的,所以我认为要么有更好的解决方案,要么我的想法不正确
请告诉我应该做什么
这些答案似乎建议在访问ISR中的全局变量时使用volatile:
,
,
,
除此之外,还有许多推荐使用的外部来源。可能我的原始信息不清楚,或者我的情况与这些不同。您不应该将所有内容都设置为volatile。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;
}
当我们查看程序集时,我们看到在正则_sum
中,编译器意识到您正在对同一指针进行两次解引用,并将其优化为仅进行一次解引用。但在volatile\u 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的地方。重申内森·奥利弗的话
您只需要在硬件可以更改变量值的变量上使用volatile,因为编译器不知道这一点。这就是volatile的作用,让编译器知道这是一个特殊的变量,可以用它不知道的方式进行更改。如果硬件无法更改您的值,则不需要volatile
但是如果你在计算一个对象,不要使用volatile。对普通对象进行计算,然后将结果复制到volatile指针
易失性和中断服务例程。
对可能被中断服务例程修改的全局变量使用volatile
是合适的。也就是说,volatile
不能与std::bitset
这样的对象一起使用,因为std::bitset
不支持volatile操作,而且std::bitset
也不容易复制
在这方面,您有两种选择:
- 使用包含易失性原语的容器(例如,
std::vector
- 编写自己的类,支持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];
}
}
我们还可以将此行为封装在一个类中,以便“签出”易失性变量:
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
应该做什么?您只需要在写入和读取诸如pin和其他被认为有副作用的东西时使用volatile。扩展此“正确处理中断”概念。我不认为<>代码> Value会做你认为它做的事情,或者如果它确实是,你将是真正的,C++对嵌入式系统越来越流行。请注意<>代码> Frase。大多数人不理解它实际上做了什么,它做的很少是他们想要的。在几乎所有的情况下,使用volatile
是一个bug,您实际上还想使用其他东西。最重要的是;volatile
并不意味着“线程安全”。它还将有效地禁用编译器优化器。另请参见:,几乎所有其他地方都说,在ISR中访问/写入的全局变量需要标记为volatile。我不明白我的情况有何不同。您能否指定:everywhere?我怀疑您有一些文章或书籍?@JVApen,以及答案在我的原始帖子中链接。请注意,C++11 ISO标准volatile关键字是不同的,并且在指定/volatile:ISO编译器选项时在MSVC中受支持。
->Microsoft默认不符合标准只有当您将地址映射到要写入的硬件时,volatile才有意义,所有其他情况都是错误s
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 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
}