如何更好地实现此autoscope模板对象?

时间:2015-08-07 20:47:54

标签: c++ c++11 visual-c++

我有一个自动作用域类型的想法,当它们离开作用域或者包含对象被破坏时将清理资源。

这就是目前的情况:

template <typename RESOURCE, typename DELETEOR>
struct autoscope
{
    RESOURCE m_resource;
    DELETEOR const& m_deleteor;

    autoscope(RESOURCE resource, DELETEOR const& deleteor)
        : m_resource(resource)
        , m_deleteor(deleteor)
    {
    }

    operator RESOURCE()
    {
        return m_resource;
    }

    ~autoscope()
    {
        m_deleteor(m_resource);
    }
};

template <typename RESOURCE, typename DELETEOR>
autoscope<RESOURCE, DELETEOR> make_autoscope(RESOURCE resource, DELETEOR deleteor)
{
    return autoscope<RESOURCE, DELETEOR>(resource, deleteor);
}

我看到/遇到的一些潜在/实际问题。

  1. 对于每个处理的资源,都会创建一个DELETOR对象或指针。我不确定优化器是否会破坏它并且只是内联DELETOR仿函数或函数指针,以便autoscope对象不会大于RESOURCE句柄。我不确定编译器是否被允许,但我不认为可以将DELETOR 作为一种类型传递给

  2. 我尝试传递HICON和函数指针DestroyIcon(),然后崩溃了。调试时显示传递给make_autoscope()的指针是0x755225e0 {user32.dll!_DestroyCursor@4},这显然是错误的。这是VC ++问题(2013)吗?或者还有其他错误吗?

  3. 我目前在VS2013中使用VC ++,但这应该是可移植的。

    修改

    好的,我已经根据评论想出了一些想法。这是我到目前为止所提出的:

    template <typename RESOURCE, typename CRTP, RESOURCE invalid>
    struct autoscope
    {
    protected:
        RESOURCE m_res;
    public:
        autoscope()
            : m_res(invalid)
        {
        }
    
        autoscope(RESOURCE res)
            : m_res(res)
        {
        }
    
        autoscope(autoscope&& move)
        {
            std::swap(move.m_res, m_res);
        }
    
        autoscope(autoscope& copy) = delete;
    
        operator RESOURCE() const
        {
            return m_res;
        }
    
        RESOURCE get() const
        {
            return m_res;
        }
    
        RESOURCE operator =(RESOURCE res)
        {
            set(res);
        }
    
        void set(RESOURCE res)
        {
            static_cast<CRTP*>(this)->delete_resource();
            m_res = res;
        }
    
        RESOURCE release()
        {
            RESOURCE result = m_res;
            m_res = invalid;
            return result;
        }
    
        ~autoscope()
        {
            static_cast<CRTP*>(this)->delete_resource();
        }
    
        operator bool() const
        {
            return m_res != (RESOURCE)0;
        }
    
        bool valid() const
        {
            return m_res != invalid;
        }
    };
    
    template <typename RESOURCE, BOOL(WINAPI *fn_delete)(RESOURCE), RESOURCE invalid = (RESOURCE)-1>
    struct autoscope_bool : public autoscope<RESOURCE, autoscope_bool<RESOURCE, fn_delete, invalid>, invalid>
    {
        typedef autoscope<RESOURCE, autoscope_bool, invalid> base;
        autoscope_bool()
            : base(invalid)
        {
        }
    
        autoscope_bool(RESOURCE res)
            : base(res)
        {
        }
    
        autoscope_bool(autoscope_bool&& move)
            : base(std::forward<autoscope_bool>(move))
        {
        }
    
        autoscope_bool(autoscope_bool& copy) = delete;
    
        void delete_resource()
        {
            if (base::m_res && base::m_res != invalid)
            {
                VERIFY(fn_delete(base::m_res));
                base::m_res = invalid;
            }
        }
    };
    
    template <typename RESOURCE, typename void(WINAPI *fn_delete)(RESOURCE), RESOURCE invalid = (RESOURCE)-1>
    struct autoscope_void : autoscope<RESOURCE, autoscope_void<RESOURCE, fn_delete, invalid>, invalid>
    {
        typedef autoscope<RESOURCE, autoscope_void, invalid> base;
        autoscope_void()
            : base(invalid)
        {
        }
    
        autoscope_void(RESOURCE res)
            : base(res)
        {
        }
    
        autoscope_void(autoscope_void&& move)
            : base(std::forward<autoscope_void>(move))
        {
        }
    
        autoscope_void(autoscope_void& copy) = delete;
    
        void delete_resource()
        {
            if (m_res != invalid)
            {
                VERIFY(fn_delete(m_res));
                m_res = invalid;
            }
        }
    };
    
    template <typename RESOURCE, typename FUNCTOR_DELETE, RESOURCE invalid = (RESOURCE)-1>
    struct autoscope_functor : autoscope<RESOURCE, autoscope_functor<RESOURCE, FUNCTOR_DELETE, invalid>, invalid>
    {
        typedef autoscope<RESOURCE, autoscope_functor, invalid> base;
    private:
        FUNCTOR_DELETE m_functor_delete;
    public:
        autoscope_functor(FUNCTOR_DELETE functor_delete)
            : autoscope(invalid)
            , m_functor_delete(functor_delete)
        {
        }
    
        autoscope_functor(RESOURCE res, FUNCTOR_DELETE functor_delete)
            : autoscope(res)
            , m_functor_delete(functor_delete)
        {
        }
    
        autoscope_functor(autoscope_functor&& move)
            : base(std::forward<autoscope_functor>(move))
            , m_functor_delete(move.m_functor_delete)
        {
        }
    
        autoscope_functor(autoscope_functor& copy) = delete;
    
        void delete_resource()
        {
            if (m_res != invalid)
            {
                m_functor_delete(m_res);
                m_res = invalid;
            }
        }
    };
    

2 个答案:

答案 0 :(得分:2)

这需要做很多工作。

  • 绝对不应将删除器存储为参考。即使您修复make_autoscope也通过引用获取删除,这也是灾难的处方。用lambda作为删除器调用make_autoscope,你的代码就会爆炸。
  • 复制和移动操作需要修复。默认的编译器生成的肯定是。这个类不应该是可复制的,必须是可构造的(make_autoscope可以工作),并且可能需要也可能不需要移动可分配。
  • 界面可以使用一些额外的成员,例如get()reset()release()
  • 我不确定是否担心事物的大小,但如果需要,可以将删除器和资源存储在压缩对中。至于内联,如果删除器是一个函数指针,并且该东西被移动,编译器可能无法内联调用。但这并不是什么新鲜事。

答案 1 :(得分:2)

c ++ 11已经拥有unique_ptr<T, Deleter>shared_ptr<T>(T*, Deleter())所需的内容。

没有任何理由在这里重新发明轮子。

例如:

#include <iostream>
#include <cstdio>
#include <memory>

auto main() -> int
{

    auto file_closer = [](FILE* fp) -> int
    {
        return std::fclose(fp);
    };

    // a unique_ptr who's custom deleter matches the signature of int fclose()
    using unique_auto_file = std::unique_ptr<FILE, int (*)(FILE*)>;
    unique_auto_file f(fopen("temp.txt", "r"), file_closer);

    // a shared_ptr to FILE
    using shared_auto_file = std::shared_ptr<FILE>;
    // construct with custom deleter (int-ignored) 
    shared_auto_file fs(fopen("temp.txt", "r"), file_closer);

    return 0;
}

注意:由于@ PeterSom的有用评论而更新