C++ 使用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

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

我标记为volatile的其中一个成员变量是std::bitset,我想调用它的非volatile方法,但我不能这样做

"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
如果您有一个可复制的类,那么您可以执行以下操作。首先,我们必须定义函数,以允许我们在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
}