Tech Off Post

Single Post Permalink

View Thread: C++0x (vs2010) question, please tell me what i'm doing wrong
  • User profile image
    Mr Crash

    Very interesting and helpful answers, thank you.

     

    I decided to see how many times i could shot myself in the foot.
    Basically if i could make something that was lighter then the suggestions.

    That is without std::shared_ptr and std::function.

    And yet again i managed to get into a fight with the compiler

    This is sandbox code so expect strangeness and inconsistencies:


    #include <windows.h> // UNREFERENCED_PARAMETER
    #include <assert.h>
    #include <cstddef>
    #include <utility>
    
    // A macro to disallow the copy constructor and operator= functions
    // This should be used in the private: declarations for a class
    #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
        TypeName(const TypeName&);               \
        void operator=(const TypeName&)
    
    // default_delete - scalar
    template<typename T>
    struct default_delete
    {
        inline void operator()(T obj) const
        {
            // empty
        }
    };
    
    // default_delete - scalar pointer
    template<typename T>
    struct default_delete<T*>
    {
        inline void operator()(T *ptr) const
        {
            delete ptr;
        }
    };
    
    // default_delete - array
    template<typename T>
    struct default_delete<T[]>
    {
        inline void operator()(T *ptr) const
        {
            delete [] ptr;
        }
    };
    
    template<typename T>
    struct default_empty {
        inline void operator()(T) const    {
            // empty
        }
    };
    
    // scalar version
    template<typename T, typename OnLeaveScopeFunc>
    class scoped {
    public:
        explicit scoped(T p = 0, OnLeaveScopeFunc func = default_delete<T>) : m_obj(p), m_onLeaveScope(func) {}
    
        ~scoped(void)
        {
            m_onLeaveScope(m_obj);
        }
    
        // Common to both pointer and array
        void reset(T p = 0)
        {
            if (m_obj != p)
            {
                m_onLeaveScope(m_obj);
                m_obj = p;
            }
        }
    
        T get(void) const
        {
            return m_obj;
        }
    
        bool operator==(const T p) const
        {
            return m_obj == p;
        }
    
        bool operator!=(const T p) const
        {
            return !(m_obj == p);
        }
    
        // Move constructor and assignment
        // This will enable make_scoped to work
        scoped(scoped &&right)
        {
            m_obj = right.m_obj;
            m_onLeaveScope = right.m_onLeaveScope;
            right.m_obj = 0;
        }
    
        scoped &operator=(scoped &&right)
        {
            scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
            return *this;
        }
    
        void swap(scoped &right)
        {
            std::swap(m_obj, right.m_obj);
            std::swap(m_onLeaveScope, right.m_onLeaveScope);
        }
    
        T release(void)
        {
            T temp = m_obj;
            m_obj = 0;
            return temp;
        }
    
    private:
        T m_obj;
        OnLeaveScopeFunc m_onLeaveScope;
    
        DISALLOW_COPY_AND_ASSIGN(scoped);
    };
    
    // scalar pointer version
    template<typename T, typename OnLeaveScopeFunc>
    class scoped<T*, OnLeaveScopeFunc> {
    public:
        typedef T element_type;
    
        explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}
    
        ~scoped(void)
        {
            m_onLeaveScope(m_ptr);
        }
    
        // Common to both pointer and array
        void reset(T *p = nullptr)
        {
            if (m_ptr != p)
            {
                m_onLeaveScope(m_ptr);
                m_ptr = p;
            }
        }
    
        T *get(void) const
        {
            return m_ptr;
        }
    
        bool operator==(const T *p) const
        {
            return m_ptr == p;
        }
    
        bool operator!=(const T *p) const
        {
            return !(m_ptr == p);
        }
    
        // Move constructor and assignment
        // This will enable make_scoped to work
        scoped(scoped &&right)
        {
            m_ptr = right.m_ptr;
            m_onLeaveScope = right.m_onLeaveScope;
            right.m_ptr = nullptr;
            right.m_onLeaveScope = m_onLeaveScope_empty;
        }
    
        scoped &operator=(scoped &&right)
        {
            scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
            return *this;
        }
    
        void swap(scoped &right)
        {
            std::swap(m_ptr, right.m_ptr);
            std::swap(m_onLeaveScope, right.m_onLeaveScope);
        }
    
        T *release(void)
        {
            T *temp = m_ptr;
            m_ptr = nullptr;
            return temp;
        }
    
        // Scalar pointer version
        T &operator*(void) const
        {
            assert(m_ptr != nullptr);
            return *m_ptr;
        }
    
        T *operator->(void) const
        {
            assert(m_ptr != nullptr);
            return m_ptr;
        }
    
    private:
        T *m_ptr;
        OnLeaveScopeFunc m_onLeaveScope;
        OnLeaveScopeFunc m_onLeaveScope_empty;
    
        DISALLOW_COPY_AND_ASSIGN(scoped);
    };
    
    // array version
    // FIXME: Any way to prevent missing [] from compiling ? Ex. scoped<int> bla(new int[])
    // or to detect it based on how it is used further down the code ?
    template<typename T, typename OnLeaveScopeFunc>
    class scoped<T[], OnLeaveScopeFunc> {
    public:
        typedef T element_type;
    
        explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func) {}
    
        ~scoped(void)
        {
            m_onLeaveScope(m_ptr);
        }
    
        // Common to both pointer and array
        void reset(T *p = nullptr)
        {
            if (m_ptr != p)
            {
                m_onLeaveScope(m_ptr);
                m_ptr = p;
            }
        }
    
        T *get(void) const
        {
            return m_ptr;
        }
    
        bool operator==(const T *p) const
        {
            return m_ptr == p;
        }
    
        bool operator!=(const T *p) const
        {
            return !(m_ptr == p);
        }
    
        // Move constructor and assignment
        // This will enable make_scoped to work
        scoped(scoped &&right)
        {
            m_ptr = right.m_ptr;
            m_onLeaveScope = right.m_onLeaveScope;
            right.m_ptr = nullptr;
        }
    
        scoped &operator=(scoped &&right)
        {
            scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
            return *this;
        }
    
        void swap(scoped &right)
        {
            std::swap(m_ptr, right.m_ptr);
            std::swap(m_onLeaveScope, right.m_onLeaveScope);
        }
    
        T *release(void)
        {
            T *temp = m_ptr;
            m_ptr = nullptr;
            return temp;
        }
    
        // Array version
        // Should we use uintptr_t instead of ptrdiff_t ?
        T &operator[](std::ptrdiff_t i) const
        {
            assert(i >= 0);
            assert(m_ptr != nullptr);
            return m_ptr[i];
        }
    
    private:
        T *m_ptr;
        OnLeaveScopeFunc m_onLeaveScope;
    
        DISALLOW_COPY_AND_ASSIGN(scoped);
    };
    
    // template argument deduction goodness
    template <typename T, typename F>
    inline scoped<T, F> make_scoped(T t, F f)
    {
        return std::move(scoped<T, F>(t, f));
    }
    
    //----------------------------------------------------
    
    inline void normal_f(int p) {
        _tprintf_s(_T("normal_f p: %d\n"), p);
    }
    
    struct normal_struct_f {
        inline void operator()(int p) const {
            _tprintf_s(_T("normal_struct_f p: %d\n"), p);
        }
    };
    
    inline void normal_fp(int *p) {
        _tprintf_s(_T("normal_fp *p: %d\n"), *p);
    }
    
    struct normal_struct_fp {
        inline void operator()(int *p) const {
            _tprintf_s(_T("normal_struct_fp *p: %d\n"), *p);
        }
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        UNREFERENCED_PARAMETER(argc);
        UNREFERENCED_PARAMETER(argv);
    
        // 2010-02-24
        auto f = [](int p) {
            _tprintf_s(_T("f p: %d\n"), p);
        };
    
        auto fp = [](int *p) {
            _tprintf_s(_T("fp *p: %d\n"), *p);
        };
    
        {
            auto t1 = make_scoped(1975, f);
            auto t2 = make_scoped(1975, normal_f);
            auto t3 = make_scoped(1975, normal_struct_f());
        }
        _tprintf_s(_T("\n")); // For output readability
        {
            auto t1 = make_scoped(new int(1975), fp);
            auto t2 = make_scoped(new int(1975), normal_fp);
            auto t3 = make_scoped(new int(1975), normal_struct_fp());
        }
        _tprintf_s(_T("\n")); // For output readability
        return 0;
    }
    


    Ok, so the problem i'm having:
    In <T*> version of 'scoped(scoped &&right)' (that's the class i've used as test subject)
    How to make 'right.m_onLeaveScope' accept the new empty function ?


    The compiler complains:
    "error C2275: 'default_empty<T>' : illegal use of this type as an expression"
    due to 'm_onLeaveScope_empty(default_empty<T>)' on line
    explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}

    I can't see why i doesn't want to work.

    "OnLeaveScopeFunc func = default_delete<T>"

    Worked fine so why doesn't that want to work ?

    I've also tried:

    "OnLeaveScopeFunc func_empty = default_empty<T>"

    'm_onLeaveScope_empty(func_empty)'

     

    explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>, OnLeaveScopeFunc func_empty = default_empty<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(func_empty) {}