C++ FIFO发布订单的RAII等效物

C++ FIFO发布订单的RAII等效物,c++,raii,C++,Raii,RAII非常舒适,我很难为资源提供等效的设计,这些资源必须以获取的相同顺序(FIFO)释放,而不是以RAII自然产生的相反顺序(堆栈)释放 在我的特定情况下,我有一个流类,如下所示: 模板 类流{ ... 公众: //生产者API T&write_acquire();//这将获取一个存储元素并将“阻塞” //直到底层资源中有可用的插槽 void write_release(&T);//这将释放存储元素,将数据传输给使用者 //消费者API T&read_acquire();//这将获取一个存储元

RAII非常舒适,我很难为资源提供等效的设计,这些资源必须以获取的相同顺序(FIFO)释放,而不是以RAII自然产生的相反顺序(堆栈)释放

在我的特定情况下,我有一个
类,如下所示:

模板
类流{
...
公众:
//生产者API
T&write_acquire();//这将获取一个存储元素并将“阻塞”
//直到底层资源中有可用的插槽
void write_release(&T);//这将释放存储元素,将数据传输给使用者
//消费者API
T&read_acquire();//这将获取一个存储元素并将“阻塞”
//直到插槽被写入并释放
void read_release(&T);//这将释放存储元素,使其可用
//对于未来的潜在需求,请编写
};
我正在考虑提供一个RAII风格的助手:

模板
类流\u wslot{
stream&s;
T&slot;
公众:
stream_wslot(stream&s):s{s},slot{s.write_acquire()}{}
~stream_wslot(){s.write_release(slot);}
运算符T&({return slot;}
T&operator=(T&val){returnslot=val;}
};
但问题是以下用法将无法正常使用:

无效测试(流和fifo){
流_wslot偶数(fifo);
奇数流(fifo);
……首先。。。
第二
//发布奇数!!!
//甚至释放
}

也就是说,我们将先释放
奇数
插槽,然后再释放
偶数
插槽。虽然我可以在
流中添加一个“重新排序”队列
,但我想知道是否有一种“干净”的方法可以将RAII推广到这些情况。

使用
std::optional
,以非常适中的成本,最小的开销提供了对构造的更多控制,以及定义良好的销毁顺序。这正是你要找的

例如:

std::optional<stream_wslot> o_even;
std::optional<stream_wslot> o_odd;

o_odd.emplace(fifo);
o_even.emplace(fifo);

auto &even=*o_even;
auto &odd=*o_odd;
std::可选o_偶数;
std::可选o_奇数;
单数进位(fifo);
o_偶数进驻(fifo);
自动和偶数=*o_偶数;
自动和奇数=*o_奇数;

从这一点开始,使用
奇数
偶数
的现有代码将很难区分两者之间的区别。总的来说:
奇数
首先构造,
偶数
第二个构造<代码>奇数首先被销毁,当离开此范围时,
偶数
第二次被销毁。相同的有效构造和销毁顺序。

您可以在另一个线程上运行队列,以异步实现FIFO逻辑。如果
wslot
对象是使用该队列的引用构造的,则它们可以在销毁时告诉它为它们执行
write_release()
,但在构造时执行常规的
write_acquire()
,必要时会阻塞


(或者,在构建过程中,他们可以在某个地方传递一个
std::future
,并通过
write_release()
ing on destruction来履行承诺。但这只是我模糊地挥手而已)。

这里有一个替代方案,它使用a并在C++11及更高版本中工作:

#include <queue>

void test(stream<float> &fifo) {
    std::queue<stream_wslot> wslots;

  #if __cplusplus >= 201703L                             // C++17 and later:
    auto& even = wslots.emplace(fifo);
    auto& odd = wslots.emplace(fifo);
  #else                                                  // C++11 and C++14:
    auto& even = (wslots.emplace(fifo), wslots.front());    
    auto& odd = (wslots.emplace(fifo), wslots.front());
  #endif

    // work with even and odd ...
}
#包括
无效测试(流和fifo){
std::队列wslots;
#如果uu cplusplus>=201703L//C++17及更高版本:
自动和偶数=wslots.emplace(fifo);
自动和奇数=wslots.emplace(fifo);
#else//C++11和C++14:
自动偶数=(wslots.emplace(fifo),wslots.front());
auto&odd=(wslots.emplace(fifo),wslots.front());
#恩迪夫
//使用偶数和奇数。。。
}
首先创建由
偶数
引用的元素,然后创建由
奇数
引用的元素


在作用域末尾,
std::queue
首先释放由
偶数
引用的元素,然后释放由
奇数
引用的元素

不给调用方直接访问
t
的权限,给他们一个
共享ptr
,每个节点都有一个到列表中下一个节点的共享ptr。然后,当第二个指针被销毁时,节点将使T保持活动状态,直到第一个指针也被销毁。哦,等等,实际的破坏仍然以错误的顺序发生。该死,难道不能使用
std::queue
?从我看到的情况来看,它会按照它们在销毁自身时输入的顺序销毁所包含的元素。这很微妙,但我认为您的类型违反了“资源”的隐含契约。因此,你不应该把它看作是一种资源(相反,考虑把整个事务打包成自己的资源,这实际上是一个队列所做的评论和回答的建议)。你尝试了答案中的任何建议吗?(从可读性的角度来看),但我的情况是嵌入式硬件,所以动态分配很昂贵/不可用。我接受了你的答案,因为这是我认为最干净的答案。但事实上,它不适用于我的用例,因为我在嵌入式设备上(动态分配太昂贵或不可用)@Nonyme-Hmm,那太糟糕了,对你没有什么帮助。队列中的类只包含两个引用,所以我希望它在系统上相当容易。也许你应该使用Sam当时展示的
std::optional
版本?