有没有办法标记在 C++ 中多个位置设置的全局变量?

问题描述 投票:0回答:1

寻找一种方法,在编译时,如果 c++ 中的多个行设置了特定变量,则标记/抛出错误。即类似

~特别~ int a;

int main(){
    while(1){
        fn1();
        fn2();
        print(a);
    }
}
void fn1(){
    a++;
}
void fn2(){
    print("hi");
}

应该可以正常编译,但是将 fn2() 更改为

void fn2(){
   a = 2;
}

应该抛出一个错误,因为“a”被 fn1 和 fn2 都修改了。 注意“a”必须在运行时经常修改(因此将其设置为 const 或创建一个在运行时设置一次后锁定的函数不起作用),它不应该被多个函数修改。

抱歉,如果这是一个重复的问题,我确实进行了搜索,但一切都是为了仅在一行上设置变量一次(即 const)。

(这个问题的其余部分只是上下文,如果您已经得到答案,请跳过)

对“你不应该这样做!”的批评“全局变量坏了!”或者有些东西没有帮助 - 这是一个非常的特定情况(多核裸机实时嵌入式软件,其执行路径必须根据来自多个不同核心/线程的任何输入完全改变) - 这有点恶心,但是像这样的事情是最干净的方法

请注意,可以使用以下方法解决此问题:

void fn1(){
   modifyA(1);
} 
void fn2(){
   modifyA(2);
} 
void modifyA(int in){
   a = in; 
}

不起作用(尽管如果这个问题的答案不能理解这个也没关系)

但是类似

#define setA(in) *some preprocessor magic*(a=in)*more magic*

(所以也许类似

#ifdef A_IS_SET #error "a set twice" #else #define A_IS_SET ; a=*whatever* #endif
,?但这不太管用) 程序中两次“setA()”会导致错误。

原因是有一个巨大的全局状态向量(约 100 个变量),每个变量只能由一个核心/线程设置,但需要被所有人读取。 每个核心/线程都有全局状态的本地“副本”,并且全局状态有一个互斥体。每个核心都会获取互斥锁,修改全局状态中的一些变量,将其中的其他所有内容复制到其本地副本(因此它与全局状态同步),然后释放互斥锁。这可能在任何时间以任何顺序发生(因此核心 0 可能会连续执行五次,然后是核心 2,然后再次是 0,然后是 1,或其他)。

简化示例:

int a;
int b;
int c;
float d;
boolean e;
mutex_t globalMutex;

int core0_a; //"coren_x" is local copy of global var x, for core/thread n (yes this part is handled better in the real thing, simplified here)
...
int core0_e;
int core1_a;
... (and so on for all other cores/vars)

fnRunByCore0(){
    getMutex(globalMutex);
    a = core0_a;                     //so a is now "owned" by core0
    c = core0_c;                     //so c is now "owned" by core0 too

    core0_b = b;
    core0_d = d;
    core0_e = e;
    releaseMutex(globalMutex);
}
fnRunByCore1(){
    getMutex(globalMutex);
    b = core1_b;                     //so b is now "owned" by core1
    d = core1_d;                     //so d is now "owned" by core1

    core1_a = a;
    core1_c = c;
    core1_e = e;
    releaseMutex(globalMutex);  
}
fnRunByCore2()

...(同样的交易,但仅设置 e)

这个问题的原因是为了确保我跟踪每个核心/线程/函数编写了哪些全局变量。理论上,我应该只需要阅读每个函数并确保它们每个只设置一次,在实践中,需要大量的肉眼检查,这只是为了双重检查。 硬件没有足够的性能开销来保存诸如索引之类的东西,该索引在运行时检查核心拥有每个变量的内容,或单独的互斥体,或类似的东西 - 每个循环运行约 100 种类型,任何运行时代码都会使表现不可接受。此外,这表明程序中存在错误,因此无论如何都应该在编译时捕获。

c++ compiler-errors global-variables c-preprocessor
1个回答
0
投票

你是对的,你原来的

modifyA
对你没有任何作用,但你可以编写一个检测并发修改的。

据我所知,目标是只有一个线程可以写入变量,这可以使用原子来完成。

// initially, a is unclaimed
std::atomic<std::thread::id> a_owner = {};
int a = 0;

void modifyA(int new_a) {
    auto me = std::this_thread::get_id();
    if (a_owner.load(std::memory_order::relaxed) == me) {
        a = new_a; // we own a, we can modify it, everything OK
    }

    // atomically do the following:
    // if (a_owner == std::thread::id{}) a_owner = me;
    std::thread::id expected = {};
    if (a_owner.compare_exchange_strong(expected, me)) {
        a = new_a; // we have become the new owner
    }
    else {
        throw ":("; // someone else already owns a, or we have failed to claim ownership
         // note: the id of the thread who stole a from us is now stored in expected
    }
}

您还可以将其包装在更好的界面中,例如

struct UniquelyModified {
private:
    std::atomic<std::thread::id> ownership = {};
    int value = {};
    
public:
    UniquelyModified() = default;

    UniquelyModified& operator=(int x) {
        value = x;
        return *this;
    }

    // TODO: delete other assignment operators, add a getter, etc.
};
© www.soinside.com 2019 - 2024. All rights reserved.