Monday, October 18, 2010

scoped_ptr

Недавно начал читать Александреску. Вот в процессе первых двух глав навалял свой scoped_ptr с custom deleter:


#ifndef __CMN_SCOPED_PTR_H__
#define __CMN_SCOPED_PTR_H__

#include "cmnNonCopyable.h"

namespace utils
{
    template < typename T, class Copyable = utils::noncopyable >
    class scoped_ptr : Copyable
    {
        struct deleter_base
        {
            virtual void release() = 0;
        } * Dt_;

        template < typename _Dx >
        class deleter
            : public deleter_base
            , Copyable
        {
            T*  p_;
            _Dx Dt_;

            template < int v > struct int2type { enum { value = v }; };

            template < typename _Type >
            class is_mem_func_ptr
            {
                template < typename T0 >
                struct is_mem_func_ptr_ { enum { res = 0 }; };

                template < typename R, typename S >
                struct is_mem_func_ptr_ < R (S::*)( ) > { enum { res = 1 }; };
            public:
                enum { res = is_mem_func_ptr_ < _Type >::res };
            };

            template < typename _DxiEx >
            void deleter_internal_ex(_DxiEx Dt, int2type < 1 >)
            {
                (*p_.*Dt)();
            }

            template < typename _DxiEx >
            void deleter_internal_ex(_DxiEx Dt, int2type < 0 >)
            {
                Dt(p_);
            }

            template < typename _Dxi >
            void deleter_internal(_Dxi Dt)
            {
                deleter_internal_ex(Dt, int2type < is_mem_func_ptr < _Dxi >::res >( ));
            }

            template < typename _Ret >
            void deleter_internal(_Ret (*Dt) ( ))
            {
                Dt();
            }

        public:
            deleter(_Dx Dt, T* p)
                : p_(p)
                , Dt_(Dt)
            { }

            virtual void release()
            {
                deleter_internal(Dt_);
            }
        };

    public:
        scoped_ptr()
            : Dt_(NULL)
            , array_(false)
            , p_(NULL)
        { }

        explicit scoped_ptr(T* p, bool array = false)
            : Dt_(NULL)
            , array_(array)
            , p_(p)
        { }

        template < typename _Dx >
        scoped_ptr(T* p, _Dx Dt)
            : Dt_(new deleter < _Dx > (Dt, p))
            , array_(false)
            , p_(NULL)
        { }

        ~scoped_ptr() { free(); }
        operator T* () { return p_; }
        T* operator->() { return p_; }

        T* release()
        {
            T* p = p_;
            p_ = NULL;

            return p;
        }

        void reset(T* p = NULL)
        {
            if (p != p_)
            {
                free();
            }

            p_ = p;
        }

        T* get() { return p_; }

    private:
        void free()
        {
            if (NULL == Dt_)
            {
                if (array_)
                {
                    delete [] p_;
                }
                else
                {
                    delete p_;
                }
            }
            else
            {
                Dt_->release();
            }
        }

    private:
        bool array_;
        T* p_;
    };
}

#ifdef SMALTI_NINJA
    #define ninja_ptr utils::scoped_ptr
#endif

#endif

//-----------------------------------------------------------------------------

Тест:

 
#include "stdafx.h"
#include

#define SMALTI_NINJA
#include "cmnScopedPtr.h"

struct ITest
{
    virtual void Release() volatile = 0;
};

class CTest : public ITest
{
    virtual void Release() volatile { delete this; }

public:
    static ITest* Create() { return new CTest; }
};

void Save(ITest* p) { }

class CSimple
{ };

int _tmain(int, _TCHAR* [])
{
    ninja_ptr < int > pInt(new int);
    ninja_ptr < CSimple > pSimple(new CSimple);
    ninja_ptr < CSimple > pSimpleArray(new CSimple[100], true);
    ninja_ptr < void >  pMalloc(::malloc(100), &::free);
    ninja_ptr < void >  pAbort((void*)0, &::abort);
    ninja_ptr < ITest > pRelease(CTest::Create(), &ITest::Release);
    ninja_ptr < ITest > pAutoSave(CTest::Create(), Save);

    return 0;
}

1 comment:

  1. Недавно обнаружил баги, в основном с освобождением deleter-а, и reset. как появится время - исправлю. Но думаю суть идеи ясна, и вообще лучше посмотреть реализацию shared_ptr - там custom deleter без багов)

    ReplyDelete