假设您有一个foo包装某种可调用对象集合的类.foo有一个成员函数run(),它迭代集合并调用每个函数对象.foo还有一个成员remove(...)将从集合中删除可调用对象.
是否有一个习惯,RAII风格的后卫,你可以把foo.run()和foo.remove(...)使得被调用驱动消除了foo.run()
将被推迟到后卫的析构函数火灾?可以用标准库中的东西来完成吗?这个模式有名字吗?
我目前的代码似乎不够优雅,所以我正在寻找最佳实践类型的解决方案.
注意:这不是关于并发性的.非线程安全的解决方案很好.问题在于重新引入和自我引用.
这是一个问题的例子,没有优雅的"延迟删除"警卫.
class ActionPlayer
{
private:
std::vector<std::pair<int, std::function<void()>>> actions_;
public:
void addAction(int id, const std::function<void()>& action)
{
actions_.push_back({ id, action });
}
void removeAction(int id)
{
actions_.erase(
std::remove_if(
actions_.begin(),
actions_.end(),
[id](auto& p) { return p.first == id; }
),
actions_.end()
);
}
void run()
{
for (auto& item : actions_) {
item.second();
}
}
};
Run Code Online (Sandbox Code Playgroud)
其他地方:
...
ActionPlayer player;
player.addAction(1, []() {
std::cout << "Hello there" << std::endl;
});
player.addAction(42, [&player]() {
std::cout << "foobar" << std::endl;
player.removeAction(1);
});
player.run(); // boom
Run Code Online (Sandbox Code Playgroud)
编辑...好吧,这是我通过RAII锁定对象看到的方式.假设递归最终终止(如果不是用户的错误),以下应该处理在运行中运行的抛出和重入调用的操作.我使用了缓存的std :: functions,因为在这段代码的实际版本中,addAction和removeAction的等价物是模板函数,它们只能存储在一个vanilla homogeneously类型的容器中.
class ActionPlayer
{
private:
std::vector<std::pair<int, std::function<void()>>> actions_;
int run_lock_count_;
std::vector<std::function<void()>> deferred_ops_;
class RunLock
{
private:
ActionPlayer* parent_;
public:
RunLock(ActionPlayer* parent) : parent_(parent) { (parent_->run_lock_count_)++; }
~RunLock()
{
if (--parent_->run_lock_count_ == 0) {
while (!parent_->deferred_ops_.empty()) {
auto do_deferred_op = parent_->deferred_ops_.back();
parent_->deferred_ops_.pop_back();
do_deferred_op();
}
}
}
};
bool isFiring() const
{
return run_lock_count_ > 0;
}
public:
ActionPlayer() : run_lock_count_(0)
{
}
void addAction(int id, const std::function<void()>& action)
{
if (!isFiring()) {
actions_.push_back({ id, action });
} else {
deferred_ops_.push_back(
[&]() {
addAction(id, action);
}
);
}
}
void removeAction(int id)
{
if (!isFiring()) {
actions_.erase(
std::remove_if(
actions_.begin(),
actions_.end(),
[id](auto& p) { return p.first == id; }
),
actions_.end()
);
} else {
deferred_ops_.push_back(
[&]() {
removeAction(id);
}
);
}
}
void run()
{
RunLock lock(this);
for (auto& item : actions_) {
item.second();
}
}
};
Run Code Online (Sandbox Code Playgroud)
添加一个标志,run表示您正在枚举 thru actions_。然后,如果removeAction使用该标志集调用 if ,则将其存储id在向量中以供以后删除。您可能还需要一个单独的向量来保存枚举时添加的操作。完成迭代后actions_,您可以删除要删除的内容并添加已添加的内容。
就像是
// within class ActionPlayer, add these private member variables
private:
bool running = false;
std::vector<int> idsToDelete;
public:
void run() {
running = true;
for (auto& item : actions_) {
item.second();
}
running = false;
for (d: idsToDelete)
removeAction(d);
idsToDelete.clear();
}
// ...
Run Code Online (Sandbox Code Playgroud)
您可以对延迟调用进行类似的更改addAction(如果任何操作可以添加操作,则需要执行此操作,因为添加可能会导致向量分配更多存储空间,从而使向量的所有迭代器无效)。