我有一个ScopedLock
类可以帮助在超出范围时自动释放锁定。但是,问题是:有时团队成员会编写无效的锁定代码,例如
{
ScopedLock(mutex); // anonymous
xxx;
}
上面的代码是错误的,因为对象是立即ScopedLock
构造和销毁的,所以未能锁定预期的区域()。我希望编译器在尝试编译此类代码时给出错误。这可以做到吗?xxx
我已经搜索g++
了警告选项,但找不到正确的选项。
我在一个代码库中看到了一个有趣的技巧,但它仅在您的 scoped_lock 类型不是模板时才有效(std::scoped_lock 是)。
#define scoped_lock(x) static_assert(false, "you forgot the variable name")
如果你正确地使用这个类,你有
scoped_lock lock(mutex);
并且由于 scoped_lock 标识符后面没有打开括号,宏不会触发并且代码将保持原样。如果你写\
scoped_lock(mutex);
宏将触发,代码将替换为
static_assert(false, "you forgot the variable name");
这将生成一条信息性消息。
如果您使用限定名称
threads::scoped_lock(mutext);
那么结果仍然无法编译,但消息不会那么好。
当然,如果你的锁是一个模板,那么坏代码就是
scoped_lock<mutex_type>(mutex);
这不会触发宏。
不,不幸的是,没有办法做到这一点,正如我在去年的一篇博文中所探讨的那样。
在其中,我得出结论:
我想这个故事的寓意是在使用s时记住这个故事。
scoped_lock
您可以尝试强制团队中的所有程序员使用宏或 range-for 技巧,但是如果您能保证在每种情况下都能做到这一点,那么您也可以保证在每种情况下都能捕捉到这个错误。
您正在寻找一种方法来在发生此特定错误时以编程方式捕获它,但没有。
您可以使用同名的类和已删除函数。不幸的是,这需要在类型之前添加“类”关键字。
class Guard
{
public:
explicit Guard(void)
{
}
};
static void Guard(void) = delete;
int main()
{
// Guard(); // Won't compile
// Guard g; // Won't compile
class Guard g;
}
To avoid this, introduce a macro which does this for you, always using the same name for the locker:
#define LOCK(mutex) ScopedLock _lock(mutex)
Then use it like this:
{
LOCK(mutex);
xxx;
}
As an alternative, Java's synchronize
block can be simulated using a macro construct: In a for-loop running always exactly once, I instantiate such a locker in the initialization statement of the for-loop, so it gets destroyed when leaving the for-loop.
However, it has some pitfalls, unexpected behavior of a break
statement being one example. This "hack" is introduced here.
Of course, none of the above methods fully avoid accidental code like your example. But if you're used to write locking mutexes using one of the two macros, it will less likely happen. As the name of the locker class will then never appear in the code except in the macro definition, you can even introduce a commit hook in a version control system to avoid committing invalid code.
AFAIK 在 gcc 中没有这样的标志。静态分析器可能更适合您的需求。
在 C++17 中,可以标记类型[[nodiscard]]
,在这种情况下,鼓励对丢弃该类型值的表达式发出警告(包括此处描述的类似于变量声明的情况)。在 C++20 中,它也可以应用于单个构造函数,只要它们中的一些会导致此类问题。
用宏替换它
#define CON2(x,y) x##y
#define CON(x,y) CON2(x,y)
#define LOCK(x) ScopedLock CON(unique_,__COUNTER__)(mutex)
用法
{
LOCK(mutex);
//do stuff
}
此宏将为锁生成唯一名称,允许锁定内部范围内的其他互斥锁