使用可变标准模板对象的方法

用户4913118

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

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

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

我想我可以复制 bitset 库并将所有内容切换为 volatile,但我认为这不是必需的,所以我认为有更好的解决方案,或者我正在错误地思考问题。

请让我知道应该怎么做。

这些答案似乎建议在访问 ISR 中的全局变量时使用 volatile:ISR 和多线程程序中的 C 'Volatile' 关键字?,

为什么在 C 中需要 volatile?,

在中断例程中使用 C++ 对象(和 volatile)的正确方法是什么?,

仅在中断期间读取变量时是否需要 volatile

微控制器编程中ISR函数中volatile关键字的使用

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

J·安东尼奥·佩雷斯

应该将所有内容都设置为 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; 
}

当我们查看程序集时,我们看到在 中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 所说的话,

您只需要在硬件可以更改变量值的变量上使用 volatile ,因为编译器无法知道这一点。这就是 volatile 的作用,让编译器知道这是一个特殊的变量,可以以它不知道的方式进行更改。如果硬件不能改变你的值,那么你就不需要 volatile。

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

易失性和中断服务例程。

volatile用于可能被中断服务程序修改的全局变量是合适的话虽如此,volatile不能与对象一起使用,std::bitset因为std::bitset不支持易失性操作,并且std::bitset不能简单地复制。

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

  • 使用包含易失性原语的容器(例如, std::vector<volatile bool>
  • 编写自己的类,支持 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];
    }
}

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

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); 
};

我们还可以将此行为封装在一个类中,以便我们可以“检查”可变变量:

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
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

返回可变对象的好方法

使用标准模板包从json对象获取前两个值

使用可变参数模板扩展

将可变参数模板完美转发到标准线程

使用可变参数模板的工厂模式?

从模板化模板类和可变参数模板中声明“容器”对象

使用libtooling获得标准模板模板参数名称

使用可变对象作为常量

clang不使用可变参数推断可变参数模板函数中的模板参数

可变模板模板?

MSVC在使用constexpr的可变参数模板方法中从基本模板参数中吞下const,如果

在Rc封装的对象中调用可变方法的标准方法是什么?

打印到函数内部的标准输出时,如何分隔可变参数模板参数

使用标准的可变范围统计

如何使用可变参数模板概括C ++中的对象创建?

有什么方法可以将成对的元组剥离为可变参数模板类型或实例化具有可变参数类型的对象?

重载可变参数模板化方法

有没有一种方法可以基于模板变量调用成员对象的可变函数

使用可变参数模板打印列表

从方法返回可变对象的不可变副本

在Freemarker模板中使用非标准类参数调用方法

在可变参数模板中使用对

使用sfinae检测可变参数模板的基类是否具有特定方法

使用不可变js,是否有更好的方法来更新不可变对象中此数组的值?

使用可变成员变量构造 Test 对象的正确方法

使用模板特化拆分可变参数包

方法链:Laravel 中可变与不可变的 php 对象

可变模板方法

使用可变参数类模板的模板参数调用可变参数函数模板?