如何初始化RAII范围内的对象,并在该范围之外使用它?
我有一个全局锁,可以使用lock()
和unlock()
来调用。
我有一个LockedObject
类型,只有在全局锁被锁定时才能初始化。
我有一个函数use_locked(LockedObject &locked_object)
,需要在解锁全局锁的情况下调用它。
使用方案是
lock();
LockedObject locked_object;
unlock();
use_locked(locked_object);
出于各种原因,我转向了全局锁的RAII封装。我想在任何地方使用它,主要是因为创建LockedObject
可能会因异常而失败。
问题在于
{
GlobalLock global_lock;
LockedObject locked_object;
}
use_locked(locked_object);
失败,因为在内部范围中创建了locked_object
。
设置(主要不重要):
#include <assert.h>
#include <iostream>
bool locked = false;
void lock() {
assert(!locked);
locked = true;
}
void unlock() {
assert(locked);
locked = false;
}
class LockedObject {
public:
LockedObject(int i) {
assert(locked);
std::cout << "Initialized: " << i << std::endl;
}
};
void use_locked(LockedObject locked_object) {
assert(!locked);
}
class GlobalLock {
public:
GlobalLock() {
lock();
}
~GlobalLock() {
unlock();
}
};
原始的非RAII方法:
void manual() {
lock();
LockedObject locked_object(123);
unlock();
use_locked(locked_object);
}
破坏RAII方法:
/*
void raii_broken_scoping() {
{
GlobalLock global_lock;
// Initialized in the wrong scope
LockedObject locked_object(123);
}
use_locked(locked_object);
}
*/
/*
void raii_broken_initialization() {
// No empty initialization
// Alternatively, empty initialization requires lock
LockedObject locked_object;
{
GlobalLock global_lock;
locked_object = LockedObject(123);
}
use_locked(locked_object);
}
*/
main
函数:
int main(int, char **) {
manual();
// raii_broken_scoping();
// raii_broken_initialization;
}
对于它的价值,在Python中我会这样做:
with GlobalLock():
locked_object = LockedObject(123)
我想要相当于那个。我在答案中提到了我目前的解决方案,但感觉很笨拙。
要执行的特定(但简化)代码如下。使用我当前基于lambda的调用:
boost::python::api::object wrapped_object = [&c_object] () {
GIL lock_gil;
return boost::python::api::object(boost::ref(c_object));
} ();
auto thread = std::thread(use_wrapped_object, c_object);
与
class GIL {
public:
GIL();
~GIL();
private:
GIL(const GIL&);
PyGILState_STATE gilstate;
};
GIL::GIL() {
gilstate = PyGILState_Ensure();
}
GIL::~GIL() {
PyGILState_Release(gilstate);
}
boost::python::api::object
必须使用GIL创建,并且必须创建没有GIL的线程。 PyGILState
结构和函数调用都是由CPython的C API给我的,所以我只能将它们包装起来。
答案 0 :(得分:3)
在堆上分配您的对象并使用一些指针:
std::unique_ptr<LockedObject> locked_object;
{
GlobalLock global_lock;
locked_object.reset(new LockedObject());
}
use_locked(locked_object);
答案 1 :(得分:2)
从我的角度来看,这是一个完整的选项列表。 optional
就是我要做的事情:
建议的后C ++ 1y optional
将解决您的问题,因为它允许您在声明后构造数据,就像基于堆的unique_ptr
解决方案一样。滚动自己,或从boost
A&#39;在范围结束时运行&#39; RAII函数存储器(使用&#39; commit&#39;)也可以使这个代码不那么疯狂,因为可以让你的锁在他们的范围内手动解除。
template<class F>
struct run_at_end_of_scope {
F f;
bool Skip;
void commit(){ if (!Skip) f(); Skip = true; }
void skip() { Skip = true; }
~run_at_end_of_scope(){commit();}
};
template<class F>
run_at_end_of_scope<F> at_end(F&&f){ return {std::forward<F>(f), false}; }
然后:
auto later = at_end([&]{ /*code*/ });
您可以later.commit();
或later.skip();
提前运行代码或跳过运行代码。
使你的RAII锁定类具有移动构造函数可以让你在另一个作用域中构建,并通过移动返回(可能被省略)。
LockedObject make_LockedObject(){
GlobalLock lock;
return {};
}
答案 2 :(得分:0)
我目前的解决方案是使用匿名函数:
void raii_return() {
LockedObject locked_object = [&] () {
GlobalLock global_lock;
return LockedObject(123);
} ();
use_locked(locked_object);
}
这种方法的优点是它避免了指针,并且由于copy elision它应该非常快。
一个缺点是LockedObject
不一定支持复制(在这种情况下use_locked
会参考)。