【问题标题】:Any RAII template in boost or C++0xboost 或 C++0x 中的任何 RAII 模板
【发布时间】:2011-01-08 11:03:24
【问题描述】:

RAII 的 boost 中是否有可用的模板。像scoped_ptrshared_ptr 这样的类基本上是在指针上工作的。这些类可以用于指针以外的任何其他资源。是否有任何模板可以与通用资源一起使用。

以某个在作用域开始时获取的资源为例,必须在作用域结束时以某种方式释放。获取和释放都需要一些步骤。我们可以编写一个模板,它需要两个(或者可能是一个对象)函子来完成这项任务。我还没有想过如何实现这一点,我只是想知道是否有任何现有的方法可以做到这一点

Edit: 一个支持 lambda 函数的 C++0x 怎么样

【问题讨论】:

  • 这是 lambda,而不是 lambda :) 已为您修复。 ;)
  • 现在不应该叫C++1x吗?
  • @LiraNuna:不是真的,因为它是一个占位符名称。它的唯一目的是方便并确保每个人都知道是什么意思。 C++1x 是模棱两可的,因为 1) 我们不习惯这个名称,以及 2) 在这十年中可能会出现不止一次的标准修订。 C++0x 是每个人都习惯的,没有别的意思了。

标签: c++ templates boost c++11 raii


【解决方案1】:

shared_ptr 提供了指定custom deleter 的可能性。当需要销毁指针时,将调用删除器并执行任何必要的清理操作。通过这种方式,可以使用这个智能指针类来管理比简单指针更复杂的资源。

【讨论】:

  • 自定义删除器很棒,但是如果您要管理的类型不是指针怎么办?这在包装像example_t data; example_new(&data); /* manipuate the data ... */ example_free(&data) 这样使用的 C 库时经常发生,如果这些对象存储在某个持久容器中,那么您还必须存储托管指针,这既不切实际又浪费。
【解决方案2】:

最通用的方法是ScopeGuard 一种(this ddj article 中的基本思想,例如在Boost.ScopeExit 中使用便利宏实现),并允许您在范围退出时执行函数或清理资源。

但老实说,我不明白你为什么想要那个。虽然我知道每次为一步获取和一步发布模式编写一个类有点烦人,但您正在谈论多步获取和发布。
如果它采取了多个步骤,在我看来,它属于一个适当命名的实用程序类,以便隐藏细节并且代码到位(从而降低错误概率)。
如果您将其与收益进行权衡,那么这几行额外的内容并不值得担心。

【讨论】:

  • 如果 Stroustrop 允许 finally 进入 c++ 生活会轻松很多(他断然拒绝,他觉得你每次都应该上课)
  • 说实话,我很少觉得需要 OP 提到的内容,因为每个重复的资源使用模式应该在实用程序类中。额外的好处 - 您不必记住资源处理细节。
  • @pm100:不,你搞错了。最后意味着我每次使用该类时都必须编写清理代码。 RAII,依赖于析构函数,意味着我可以为每个类编写清理代码一次。怎么终于又是个好主意了?
  • @jalf:阿门!最后是可憎的!
  • 当您想要编写只出现在一个地方并且在同一资源的不同用户之间不一致的清理代码时,这是一个好主意。即,从不。不,我的意思是:例如,当您想为某事添加一些快速跟踪或编写事务时(这几乎总是唯一的并且只在一个地方使用)。显然,您可以编写一个类来表示您的事务,但如果您 必须 这样做,它可能会变得有点企业化,因此各种范围保护都很好(甚至更好),除非您终于习惯了学习编码风格并且不舒服。
【解决方案3】:

一个更通用、更高效(不通过函数指针调用)的版本如下:

#include <boost/type_traits.hpp>

template<typename FuncType, FuncType * Func>
class RAIIFunc
{
public:
   typedef typename boost::function_traits<FuncType>::arg1_type arg_type;
   RAIIFunc(arg_type p) : p_(p) {}
   ~RAIIFunc() { Func(p_); }
   arg_type & getValue() { return p_; }
   arg_type const & getValue() const { return p_; }
private:
   arg_type p_;
};

使用示例:

RAIIFunc<int (int), ::close> f = ::open("...");

【讨论】:

  • 我将此解决方案通过实际编译并喜欢它。我确实需要做一些小改动,但没有什么可怕的。
【解决方案4】:

我不得不承认我没有真正理解这一点。从头开始编写 RAII 包装器已经非常简单了。使用某种预定义的包装器可以节省很多工作:

struct scoped_foo : private boost::noncopyable {
  scoped_foo() : f(...) {}
  ~scoped_foo() {...}

  foo& get_foo() { return f; }

private:
  foo f;
};

现在,... 本质上是如果您使用某种通用 RAII 模板必须手动填写的位:创建和销毁我们的 foo 资源。没有它们,真的就没有多少了。几行样板代码,但它太少了,似乎不值得将其提取到可重用的模板中,至少目前不是。通过在 C++0x 中添加 lambda,我们可以非常简洁地编写用于创建和销毁的函子,以便编写这些函子并将它们插入可重用的模板中可能是值得的。但在那之前,似乎麻烦多于价值。如果您要定义两个函子以插入 RAII 模板,那么您已经编写了大部分样板代码两次

【讨论】:

  • lambdas,是的,我就是这么想的。很抱歉在我的问题中没有提到这一点(虽然我在想)
  • 据我所知,C++0x 没有定义这样的模板。也许因为它做起来很简单,所以没有多大意义。也许他们正在等待 Boost 或其他第三方定义一个,并在标准化之前让它在现实世界中证明自己。目前,我们还不知道 1) 是否值得付出努力,以及 2) 模板是否应该能够处理任何不明显的问题。
【解决方案5】:

我正在考虑类似的事情:

template <typename T>
class RAII {
    private:
        T (*constructor)();
        void (*destructor)(T);
    public:
        T value;
        RAII(T (*constructor)(), void (*destructor)(T)) : 
                    constructor(constructor), 
                    destructor(destructor) {
            value = constructor();
        }
        ~RAII() {
            destructor(value);
        }
};

并像这样使用(以 OpenGL 的 GLUquadric 为例):

RAII<GLUquadric*> quad = RAII<GLUquadric*>(gluNewQuadric, gluDeleteQuadric);
gluSphere(quad.value, 3, 20, 20)

【讨论】:

    【解决方案6】:

    这是另一个 C++11 RAII 助手:https://github.com/ArtemGr/libglim/blob/master/raii.hpp

    它在销毁时运行一个 C++ 仿函数:

    auto unmap = raiiFun ([&]() {munmap (fd, size);});
    

    【讨论】:

    • 一个非常危险的实现:它需要发生复制/移动省略。如果没有发生,RAIIFun 对象的析构函数会运行不止一次!
    • @j6t 它使用完美转发,并且大多数肯定 not 需要省略才能工作。如果您看到一些极端情况,它运行函子两次,请提供 MCVE。
    • 很难提供 MCVE,因为复制省略不是保证,而是编译器的一个选项。当函数raiiFun 返回RAIIFun 类型的对象时,就会出现问题。如果没有省略复制/移动,则在 return 完成之前调用析构函数。当从函数结果初始化局部变量时,可能会或可能不会省略另一个复制/移动:如果没有删除临时变量,则调用其析构函数。在 C++17 中,我们得到保证复制省略,问题就消失了。
    • @j6t IIRC,return RAIIFun&lt;Fun&gt; (fun) 中没有副本,因为RAIIFun&lt;Fun&gt; (fun) 已经是一个右值。复制省略处理不同的用例,例如auto f = RAIIFun&lt;Fun&gt; (fun); return f;。当您返回右值时,不需要复制省略。operator = 中也没有副本,因为我们使用 = default; 构造函数。
    • 虽然-std=c++11 -O0 -fno-elide-constructors gcc 调用了三次函子是事实,所以确实有可能用这个来打自己的脚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    相关资源
    最近更新 更多