Thread: Assignment issues

  1. #1
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654

    Assignment issues

    After the last problem, I encountered another.
    Continuing on the shared pointer, I cannot assign the right-hand side m_ptr to the left-hand side because it's a templated-type pointer (see code).
    So, I wrapped the pointer in a struct, yet I still cannot seem to get it right. I was hoping anyone with some good eyes or some free time could help me with it.

    Minimal code snippet:
    Code:
    #ifndef MAD_SHARED_PTR_175451_H
    #define MAD_SHARED_PTR_175451_H
    
    #ifndef NULL
    #define NULL 0
    #endif
    const int nullptr = NULL;
    #include <assert.h>
    
    class nullptr_t {};
    
    template<int Bits> class ptr_thread_dangerous
    {
    };
    
    
    class Derived;
    
    template<typename T, template<int> class thread_safety = ptr_thread_dangerous, int Bits = 32> class mad_shared_ptr
    {
    protected:
    	template<typename T, int Bits> struct sptr
    	{
    
    	};
    
    	template<typename T, int Bits> class sptr_wrapper
    	{
    	public:
    		sptr_wrapper(sptr<T, Bits>* p): m_p(p) {}
    		/*template<typename OtherT> */sptr_wrapper& operator = (const sptr_wrapper<Derived, 32>& rhs)
    		{
    			m_p->p = rhs.p;
    			m_p->RefCount = rhs.RefCount;
    			m_p->pDeleter = rhs.pDeleter;
    		}
    
    	protected:
    		sptr<T, Bits>* m_p;
    	};
    
    	typedef sptr<T, Bits> ptr_t;
    
    public:
    	mad_shared_ptr(): m_ptr(nullptr) { }
    
    	template<typename Other> mad_shared_ptr(Other* p): m_ptr(nullptr) { *this = p; }
    
    	template<typename Other> mad_shared_ptr(const mad_shared_ptr<Other>& rhs): m_ptr(nullptr) { *this = rhs; }
    
    	template<typename Other> mad_shared_ptr& operator = (const mad_shared_ptr<Other>& rhs)
    	{
    		m_ptr = rhs.m_ptr;
    		return *this;
    	}
    
    protected:
    	
    	sptr_wrapper<T, Bits> m_ptr;
    
    	template<typename Other, template<int> class thread_safety, int Bits> friend class mad_shared_ptr;
    };
    
    #endif // MAD_SHARED_PTR_175451_H
    Test code:
    Code:
    #include <... mad_shared_ptr.h>
    
    class Base {};
    class Derived: public Base {};
    
    int main()
    {
    	mad_shared_ptr<Base> p;
    	mad_shared_ptr<Derived> p2 = new Derived();
    	p = p2;
    }
    And the error would be:
    1>error C2679: binary '=' : no operator found which takes a right-hand operand of type 'const mad_shared_ptr<T>::sptr_wrapper<T,Bits>' (or there is no acceptable conversion)
    1> with
    1> [
    1> T=Derived,
    1> Bits=32
    1> ]
    1> could be 'mad_shared_ptr<T>::sptr_wrapper<T,Bits> &mad_shared_ptr<T>::sptr_wrapper<T,Bits>::opera tor =(const mad_shared_ptr<T>::sptr_wrapper<Derived,32> &)'
    1> with
    1> [
    1> T=Base,
    1> Bits=32
    1> ]
    1> or 'mad_shared_ptr<T>::sptr_wrapper<T,Bits> &mad_shared_ptr<T>::sptr_wrapper<T,Bits>::opera tor =(const mad_shared_ptr<T>::sptr_wrapper<T,Bits> &)'
    1> with
    1> [
    1> T=Base,
    1> Bits=32
    1> ]
    1> while trying to match the argument list '(mad_shared_ptr<T>::sptr_wrapper<T,Bits>, const mad_shared_ptr<T>::sptr_wrapper<T,Bits>)'
    1> with
    1> [
    1> T=Base,
    1> Bits=32
    1> ]
    1> and
    1> [
    1> T=Derived,
    1> Bits=32
    1> ]
    Basically it's saying it can't find an assignment operator that takes sptr_wrapper<Base, 32> (which the lhs should be, and is, according to the error) and a sptr_wrapper<Derived, 32> (the very exact operator I tried adding).

    If anyone spots a problem, I'd be grateful for any help.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Firstly you have lots of problems shadowing template parameters.

    But then the problem still remains unless I just lift the nested classes out of the mad_shared_ptr class.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Ah, I wonder why it works if I move them outside?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Also:

    1) The code for 'sptr' in your example is incomplete
    2) How exactly does class 'Derived' tie into 'mad_shared_ptr'?
    3) 'mad_shared_ptr' has no assignment operator for a pointer to 'T'.
    4) The purpose of 'sptr_wrapper' is unclear. Can you explain that? Further, where is it supposed to get a pointer to an 'sptr', anyway?
    5) Template functions expecting another type of 'mad_shared_ptr' should forward *all* of the template parameters - not just the first one.
    6) Your back to the same problem you had earlier, this time with 'sptr_wrapper' (only now in a more complicated context). Namely, a mad_shared_ptr<T, thread_safety, Bits>::sptr_wrapper<T2, Bits2> with different parameters will, of course, yield a completely different type, so the protected qualifier prevents access.
    7) The idea of assigning, say, a mad_shared_ptr< float > to a mad_shared_ptr< string > doesn't even really make sense. How is that supposed to work?

    Lot's of confusing issues, IMO. Maybe you could describe more clearly what you're wanting to do?

  5. #5
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Sebastiani View Post
    1) The code for 'sptr' in your example is incomplete
    3) 'mad_shared_ptr' has no assignment operator for a pointer to 'T'.
    Because this is a minimal code example. The real code does not have these issues.

    2) How exactly does class 'Derived' tie into 'mad_shared_ptr'?
    Because it's a test to get it working right. Will remove when it works fine.


    4) The purpose of 'sptr_wrapper' is unclear. Can you explain that? Further, where is it supposed to get a pointer to an 'sptr', anyway?
    Look at sptr. It has two template arguments: T and Bits.
    This structure contains the actual reference counting, which is shared among all similar pointers.
    Hence, in the assignment operator, I would need to duplicate the pointer address from the rhs to the lhs.
    But that's impossible since sptr<Base, 32> != sptr<Derived, 32>.
    What do we do then? We need a function that makes it possible to assign the rhs to lhs if and only if it's possible to assign rhs.m_ptr->p to lhs.m_ptr->p (the actual pointee it's encapsulating). This should work if rhs's pointer type is derived from lhs's pointer type.
    Creating a global assignment operator is not possible, so I made a wrapper so that I could overload operator = instead.


    5) Template functions expecting another type of 'mad_shared_ptr' should forward *all* of the template parameters - not just the first one.
    Not 100% sure what you mean.

    6) Your back to the same problem you had earlier, this time with 'sptr_wrapper' (only now in a more complicated context). Namely, a mad_shared_ptr<T, thread_safety, Bits>::sptr_wrapper<T2, Bits2> with different parameters will, of course, yield a completely different type, so the protected qualifier prevents access.
    Yes, but we solved that with friends earlier, didn't we? Yes, we did.

    7) The idea of assigning, say, a mad_shared_ptr< float > to a mad_shared_ptr< string > doesn't even really make sense. How is that supposed to work?
    It does not.
    However, the idea is to use the compiler as a tool here.
    In the end, it should be assigning a string* to a float* and obviously that won't work.
    But if we assign a mad_shared_ptr<Derived> to a mad_shared_ptr<Base>, then in the end we should get an assignment of Derived* to Base*.
    This is the idea.

    Lot's of confusing issues, IMO. Maybe you could describe more clearly what you're wanting to do?
    Basically, what I need is what I described above.
    I need to assign a mad_shared_ptr<Derived> to a mad_shared_ptr<Base>.
    I would be happy to post the full code, that you may examine it.
    You should be aware that it does rely on boost. Although I could make efforts to reduce that dependancy.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  6. #6
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Because this is a minimal code example. The real code does not have these issues.
    Okay, I see.

    Because it's a test to get it working right. Will remove when it works fine.
    Ah, okay. At any rate, it doesn't seem to help the situation any, so it may as well just be removed altogether.

    Look at sptr. It has two template arguments: T and Bits.
    This structure contains the actual reference counting, which is shared among all similar pointers.
    Hence, in the assignment operator, I would need to duplicate the pointer address from the rhs to the lhs.
    But that's impossible since sptr<Base, 32> != sptr<Derived, 32>.
    What do we do then? We need a function that makes it possible to assign the rhs to lhs if and only if it's possible to assign rhs.m_ptr->p to lhs.m_ptr->p (the actual pointee it's encapsulating). This should work if rhs's pointer type is derived from lhs's pointer type.
    Creating a global assignment operator is not possible, so I made a wrapper so that I could overload operator = instead.
    Hmm, still a bit confused on that one...

    Not 100% sure what you mean.
    Well, for example, consider:

    Code:
    
    template < typename A, typename B = int, typename C = int >
    struct foo
    {    };
    
    template < typename A >
    void bar( foo< A > const& )
    {    }
    
    int main( void )
    {
        bar( foo< int, double, double >( ) );
    }
    Invoking 'bar' forces the compiler to forward the default template parameters, so it fails to compile.

    Yes, but we solved that with friends earlier, didn't we? Yes, we did.
    True, but then you should probably incorporate that into your example, so that we know that it isn't an issue.

    It does not.
    However, the idea is to use the compiler as a tool here.
    In the end, it should be assigning a string* to a float* and obviously that won't work.
    But if we assign a mad_shared_ptr<Derived> to a mad_shared_ptr<Base>, then in the end we should get an assignment of Derived* to Base*.
    This is the idea.
    I see. But wouldn't it be sufficient to just ensure that a mad_shared_ptr<Base> is used, since that should accept a pointer to a Derived?

    Basically, what I need is what I described above.
    I need to assign a mad_shared_ptr<Derived> to a mad_shared_ptr<Base>.
    I would be happy to post the full code, that you may examine it.
    You should be aware that it does rely on boost. Although I could make efforts to reduce that dependancy.
    In this case, yes, I think the complete code would be most helpful.

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I took the liberty of trying another approach.
    I still get the pesky access errors, however. Don't know if it will yield more errors beyond what I get and if any linker errors.
    Anyway, I did promise the code so you could see what's going on, so here it is:
    Code:
    #ifndef MAD_SHARED_PTR_175451_H
    #define MAD_SHARED_PTR_175451_H
    
    #ifndef NULL
    #define NULL 0
    #endif
    const int nullptr = NULL;
    //#include <boost/cstdint.hpp>
    #include <memory>
    #include <assert.h>
    
    namespace boost
    {
    	typedef unsigned long   uint32_t;
    	typedef unsigned long long uint64_t;
    }
    
    class nullptr_t {};
    
    template<typename T> T* mad_ptr_clone(const T* pOld) { return smad_ptr_int<T, boost::is_polymorphic<T>::value>::Clone(pOld); }
    
    template<typename T, bool Polymorphic> struct smad_ptr_int
    {
    	static T* Clone(const T* pOld) { return pOld->Clone(); } // IsPolymorphic = yes
    };
    template<typename T> struct smad_ptr_int<T, false>
    {
    	static T* Clone(const T* pOld) { return new T(*pOld); } // IsPolymorphic = no
    };
    
    template<int N> struct ptr_bits { /*typedef current_amount_of_bits_not_supported rf_type;*/ };
    template<> struct ptr_bits<32> { typedef boost::uint32_t rf_type; };
    template<> struct ptr_bits<64> { typedef boost::uint64_t rf_type; };
    
    #if _WIN32_WINNT >= 0x600 && WINVER >= 0x600
    	#include <windows.h>
    
    	template<int Bits> class ptr_thread_safe { };
    	template<> class ptr_thread_safe<32> 
    	{
    	public:
    		static void Increment(void* p) { InterlockedIncrement((LONG*)p); }
    		static void Decrement(void* p) { InterlockedDecrement((LONG*)p); }
    	};
    	template<> class ptr_thread_safe<64> 
    	{
    	public:
    		static void Increment(void* p) { InterlockedIncrement64((LONGLONG*)p); }
    		static void Decrement(void* p) { InterlockedDecrement64((LONGLONG*)p); }
    	};
    #endif
    
    template<int Bits> class ptr_thread_dangerous
    {
    public:
    	template<typename T> static void Increment(T* p) { (*p)++; }
    	template<typename T> static void Decrement(T* p) { (*p)--; }
    };
    
    class Derived;
    
    template<typename T, int Bits> struct sptr;
    template<typename T, int Bits, typename OtherT> sptr<T, Bits>* CastToSptrT(sptr<OtherT, Bits>* p)
    {
    	T* pTest = p->p; // Test if Other is implicitly convertable to T.
    	return (sptr<T, Bits>*)p;
    }
    	
    template<typename T, int Bits> struct sptr
    {
    	template<typename T> class deleter_base { public: virtual void destroy(T* p) = 0; };
    	template<typename T, typename deleter_t> class deleter: public deleter_base<T>
    	{
    	public:
    		deleter(deleter_t delete_fnc): m_delete_fnc(delete_fnc) { }
    		virtual void destroy(T* p) { m_delete_fnc(p); }
    
    	private:
    		deleter_t m_delete_fnc;
    	};
    
    	sptr(): p(nullptr), RefCount(0), pDeleter(nullptr) { }
    
    	T* p;
    	typename ptr_bits<Bits>::rf_type RefCount;
    	deleter_base<T>* pDeleter;
    
    	template<typename T, int Bits, typename OtherT> friend sptr<T, Bits>* CastToSptrT <> (sptr<OtherT, Bits>* p);
    };
    
    //template<typename T, int Bits> class sptr_wrapper
    //{
    //public:
    //	sptr_wrapper(sptr<T, Bits>* p): m_p(p) {}
    //	template<typename OtherT> sptr_wrapper& operator = (const sptr_wrapper<OtherT, Bits>& rhs)
    //	{
    //		assert(m_p);
    //		assert(rhs.m_p);
    //		m_p->p = rhs.m_p->p;
    //		m_p->RefCount = rhs.m_p->RefCount;
    //		m_p->pDeleter = rhs.m_p->pDeleter;
    //	}
    //
    //	sptr<T, Bits>* operator -> () { assert(m_p); return m_p; }
    //
    //	sptr<T, Bits>* get() { assert(m_p); return m_p; }
    //	void set(sptr<T, Bits>* p) { assert(p); m_p = p; }
    //
    //protected:
    //	sptr<T, Bits>* m_p;
    //	template<typename T, int Bits> friend class sptr_wrapper;
    //};
    
    template<typename T, template<int> class thread_safety = ptr_thread_dangerous, int Bits = 32> class mad_shared_ptr
    {
    protected:
    	typedef void (mad_shared_ptr::* bool_type)() const;
    
    	typedef sptr<T, Bits> ptr_t;
    
    public:
    	mad_shared_ptr(): m_ptr(nullptr) { }
    
    	template<typename Other> mad_shared_ptr(Other* p): m_ptr(nullptr) { *this = p; }
    
    	template<typename Other, typename Deleter> mad_shared_ptr(Other* p, Deleter delete_fnc): m_ptr(nullptr)
    	{
    		std::auto_ptr< ptr_t::deleter_base<T> > pDeleter( new ptr_t::deleter<T, Deleter>(delete_fnc) );
    		*this = p;
    		m_ptr->pDeleter = pDeleter.release();
    	}
    
    	mad_shared_ptr(const mad_shared_ptr& rhs): m_ptr(nullptr) { *this = rhs; }
    
    	template<typename Other> mad_shared_ptr(const mad_shared_ptr<Other>& rhs): m_ptr(nullptr) { *this = rhs; }
    
    	mad_shared_ptr(const nullptr_t&): m_ptr(nullptr) { }
    
    	~mad_shared_ptr() { DecRefCount(m_ptr); }
    
    	bool unique() const { return (m_ptr->RefCount == 1); }
    
    	void make_unique()
    	{
    		if (unique())
    			return;
    		
    		std::auto_ptr<ptr_t> pNewPtr(new ptr_t);
    		pNewPtr->p = mad_ptr_clone(m_ptr->p);
    		pNewPtr->RefCount = 1;
    		pNewPtr->pDeleter = m_ptr->pDeleter;
    		DecRefCount(m_ptr);
    		m_ptr = pNewPtr.release();
    	}
    
    	void release()
    	{
    		DecRefCount();
    		m_p = nullptr;
    		m_RefCount = nullptr;
    		m_deleter = nullptr;
    	}
    
    	template<typename Other> mad_shared_ptr& operator = (Other* rhs)
    	{
    		if (m_ptr && rhs == m_ptr->p)
    			return *this;
    
    		ptr_t* pOldPtr = m_ptr;
    		std::auto_ptr<ptr_t> pNewPtr(new ptr_t);
    
    		pNewPtr->p = rhs;
    		pNewPtr->RefCount++;
    		m_ptr = pNewPtr.release();
    
    		DecRefCount(pOldPtr);
    		return *this;
    	}
    
    	mad_shared_ptr& operator = (const mad_shared_ptr& rhs)
    	{
    		return operator = <T>(rhs);
    	}
    
    	template<typename Other> mad_shared_ptr& operator = (const mad_shared_ptr<Other>& rhs)
    	{
    		if (rhs == *this)
    			return *this;
    		//T* pTest = rhs.m_ptr->p; // Test if Other is implicitly convertable to T.
    		DecRefCount(m_ptr);
    		m_ptr = CastToSptrT<T>(rhs.m_ptr);
    		IncRefCount(m_ptr);
    		return *this;
    	}
    
    	mad_shared_ptr& operator = (const nullptr_t&)
    	{
    		DecRefCount(m_ptr);
    		m_ptr = nullptr;
    		return *this;
    	}
    
    	T& operator * () const { assert(m_ptr); return *m_ptr->p; }
    	T* operator -> () const { assert(m_ptr); return m_ptr->p; }
    
    	template<typename rhs_t>
    	friend bool operator == (const mad_shared_ptr& lhs, const mad_shared_ptr<rhs_t>& rhs)
    	{
    		return (lhs.m_ptr == CastToSptrT<T>(rhs.m_ptr));
    	}
    
    	template<typename rhs_t>
    	friend bool operator != (const mad_shared_ptr& lhs, const mad_shared_ptr<rhs_t>& rhs)
    	{
    		return !(lhs == CastToSptrT(rhs));
    	}
    
    	operator bool_type() const 
    	{
    		return m_ptr && m_ptr->p ? &mad_shared_ptr::this_type_does_not_support_comparisons : nullptr;
    	}
    
    protected:
    	void IncRefCount(sptr<T, Bits>* ptr)
    	{
    		thread_safety<Bits>::Increment(&ptr->RefCount);
    	}
    
    	void DecRefCount(sptr<T, Bits>*& ptr)
    	{
    		if (ptr == nullptr)
    			return;
    		thread_safety<Bits>::Decrement(&ptr->RefCount);
    		if (ptr->RefCount == 0)
    		{
    			if (ptr->pDeleter)
    				ptr->pDeleter->destroy(ptr->p);
    			else
    				delete ptr->p;
    			delete ptr;
    			ptr = nullptr;
    		}
    	}
    
    	void this_type_does_not_support_comparisons() const {}
    
    	sptr<T, Bits>* m_ptr;
    
    	template<typename OtherT, template<int> class thread_safety, int Bits> friend class mad_shared_ptr;
    };
    
    #endif // MAD_SHARED_PTR_175451_H
    Last edited by Elysia; 01-13-2010 at 12:59 PM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Menu
    By Krush in forum C Programming
    Replies: 17
    Last Post: 09-01-2009, 02:34 AM
  2. Assignment Operator, Memory and Scope
    By SevenThunders in forum C++ Programming
    Replies: 47
    Last Post: 03-31-2008, 06:22 AM
  3. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  4. Help with a pretty big C++ assignment
    By wakestudent988 in forum C++ Programming
    Replies: 1
    Last Post: 10-30-2006, 09:46 PM
  5. Replies: 1
    Last Post: 10-27-2006, 01:21 PM