LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 04-13-2007, 09:32 AM   #1
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
lvalue operator / consumable objects


Take the following bit of code. My first question is do explicit constructors prohibit using = in construction? GCC accepts this code, but Comeau online seems to want to construct the 'Consumable's by first creating an rvalue-'Consumable'. Does the C++ standard require () instead of = in this case?
Code:
#include <iostream>


class Consumable
{
public:
    explicit Consumable(int *pPointer) :
	Pointer(pPointer) {}

    Consumable(Consumable &eEqual) :
	Pointer(eEqual.Pointer)
    { eEqual.Pointer = 0; }

    operator Consumable&() { return *this; }

    int Value() const { return Pointer? *Pointer : 0; }

    ~Consumable() { delete Pointer; }

private:
    explicit Consumable(const Consumable&) {}
    Consumable &operator = (const Consumable&) { return *this; }

    int *Pointer;
};


Consumable Transfer(Consumable oOld)
{ return oOld; }


int main()
{
    int *Value = new int(100);

    Consumable Test1 = Value;
    std::cout << "Test1: " << Test1.Value() << "\n";

    Consumable Test2 = Test1;
    std::cout << "Test2: " << Test2.Value() << "\n";
    std::cout << "Test1: " << Test1.Value() << "\n";

    Consumable Test3 = Transfer(Test2);
    std::cout << "Test3: " << Test3.Value() << "\n";
    std::cout << "Test2: " << Test2.Value() << "\n";
    std::cout << "Test1: " << Test1.Value() << "\n";
}
My second question is what is your opinion of an object which is consumed by a function it is used in? In a library I'm building I use the pattern above using single-use objects that are consumed when passed to a function. This is a memory management measure: certain functions take/return a consumable object to allow the user to modify the structure of dynamic objects. The idea of the object is that a user can store one temporarily while they figure out where to put it, but if their temp goes out of scope then the enclosed pointer is deleted if the object hasn't been consumed by another function. In other words, a function returns a pointer to the user under the condition they return it to something "responsible". If they don't then that pointer will be deleted, and if they don't even store the return then it is destroyed at the end of the call. Thanks.
ta0kira

PS I am not using auto_ptr for these reasons:
1) basic lib will have no STL or explicit libc ties
2) cannot have an assignment operator due to ambiguity
3) only certain objects may release the pointer
4) the "don't delete me" criteria is type-specific
 
Old 04-13-2007, 10:02 AM   #2
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
I would say g++ should not be allowing this to compile what compiler warning level are you using? also looks like you are reinventing a scoped pointer class.

[edit]
Here is copy of the scoped shared pointer class I use, you would need to adjust it if you wanted to use it tho as the release function releases it reference to the pointer not releases the actual pointer from the container to the user.

Code:
///////////////////////////////////////////////////////////////////////////////
///  shared_ptr.h
///  A very simple implementation of a shared scope pointer.
///  On going out of scope if decrements the counter so you must store it if
///  you want to use it later/
///  @remarks 
///  It should look and feel like a normal pointer if you see anything
///  that does not seem correct, please let me know.
///  Warning!!!!!!!!!!!!!!!!  
///  do not delete/null the pointer returned by ptr() you do not own it.
///  We could make it a T const* but this may cause problems????????????????
///  Added a release which will decrement the counter and delete if its zero.
///  @author Liam Devine @date 17/02/2007
///  update:
///  added static and dynamic casting
///////////////////////////////////////////////////////////////////////////////
#ifndef SHARED_PTR_H_
#	define SHARED_PTR_H_
#	include <cassert>
#	include <stdexcept>

namespace LDR
{
	//removed the ref counter to outside of the shared_ptr scope as 
	//when casting the raw underlying pointer it would require another cast
	struct Counter
	{
		Counter():m_amount(0){}
		int m_amount;
	};

	//forward declarations
	template<typename T>class Shared_ptr;
	template<typename TO,typename FROM>Shared_ptr<TO> sp_dynamic_cast(FROM rhs);
	template<typename TO,typename FROM>Shared_ptr<TO> sp_static_cast(FROM rhs);

	template<typename T>
	class Shared_ptr
	{
	public:
		//struct Counter
		//{
		//	Counter():m_amount(0){}
		//	int m_amount;
		//};
	public:
		Shared_ptr():m_ptr(0),m_count(0){}

		Shared_ptr(T* pointer):m_ptr(pointer),m_count(new Counter)
		{
			assert(m_ptr && m_count);
			++m_count->m_amount;
		}
		Shared_ptr(T* pointer, Counter* c):m_ptr(pointer),m_count(c)
		{
			assert(m_ptr && m_count);
			++m_count->m_amount;
		}
		Shared_ptr(Shared_ptr<T> const& rhs):m_ptr(rhs.m_ptr),m_count(rhs.m_count)
		{
			assert(rhs.m_ptr && rhs.m_count);
			++m_count->m_amount;
		}
		Shared_ptr<T>& operator = (Shared_ptr<T> const& rhs)
		{
			assert(rhs.m_ptr && rhs.m_count);
			m_ptr = rhs.m_ptr;
			m_count = rhs.m_count;
			++m_count->m_amount;
			return *this;
		}
		~Shared_ptr()
		{
			if(m_count)
			{
				if(m_count->m_amount>0)
				{
					--m_count->m_amount;
					if(m_count->m_amount==0)
					{
						delete m_ptr;m_ptr=0;
						delete m_count;m_count=0;
					}
				}
			}
		}
		T* operator->()const
		{
			return m_ptr;
		}
		T& operator*()const
		{
			return *m_ptr;
		}
		T* ptr()const
		{
			return m_ptr;
		}
		T& ref()const
		{
			return *m_ptr;
		}
		bool operator ==(Shared_ptr<T> const& rhs)const
		{
			return m_ptr == rhs.m_ptr;
		}
		bool operator !=(Shared_ptr<T> const& rhs)const
		{
			return ! (m_ptr == rhs.m_ptr);
		}
		operator bool ()const
		{
			return m_ptr !=0;//&& m_count && m_count->m_amount != 0;
		}
		int count()const
		{
			return m_count ? m_count->m_amount : 0;
		}
		void release()
		{
			if(m_count)
			{
				if(m_count->m_amount > 0)
				{
					--m_count->m_amount;
				}
				else
				{
					delete m_count;
					if(m_ptr)delete m_ptr;
				}
				m_count=0;
				m_ptr=0;
			}
		}
	private:
		//casting friends
		template<typename TO,typename FROM>friend Shared_ptr<TO> sp_dynamic_cast(FROM);
		template<typename TO,typename FROM>friend Shared_ptr<TO> sp_static_cast(FROM);
		T* m_ptr;
		Counter* m_count;
	};


	template<typename TO,typename FROM>
		Shared_ptr<TO> sp_static_cast(FROM rhs)
	{	
		Shared_ptr<TO> r ( static_cast<TO*>( rhs.m_ptr),rhs.m_count );
		return r;
	}


	template<typename TO,typename FROM>
		Shared_ptr<TO> sp_dynamic_cast(FROM rhs)
	{
		TO* p = dynamic_cast<TO*>(rhs.m_ptr);
		if(!p){ throw std::runtime_error("dynamic_cast error"); }
		return Shared_ptr<TO> (p,rhs.m_count);
	};

}


#endif//SHARED_PTR_H_

Last edited by dmail; 04-13-2007 at 10:29 AM.
 
Old 04-13-2007, 10:28 AM   #3
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
I am using -Wall and don't get any warnings (I rewrote this from memory but it's essentially the same.) I did find an answer for the first question: 'explicit' does require (), however I think the line 'Consumable Test3 = Transfer(Test2);' should implicitly call 'operator Consumable&()' (with GCC it does.)

I do need a custom class in this case because deletion is conditional based on the result of a virtual function called on the pointer. What I'd really like to know is if it's too non-standard that a 'Consumable' passed to a function is no longer usable. Again, I don't want any explicit external dependencies. Thanks.
ta0kira

PS A scoped pointer can't be copied, however I need mutating copying in the way auto_ptr has and no assignment operator (whether default or mutating.) A shared pointer is too much overhead and the reference counting is redundant in the case of my lib.

Last edited by ta0kira; 04-13-2007 at 10:50 AM.
 
Old 04-13-2007, 10:51 AM   #4
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
This is the output g++ gives me
Quote:
$ g++ taokira.cpp -Wall -ansi -pedantic -o taokira
taokira.cpp: In function `int main()':
taokira.cpp:36: error: conversion from `int*' to non-scalar type `Consumable' re
quested
 
Old 04-13-2007, 10:58 AM   #5
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
Yes, I guess I mis-typed when rewriting that part (or maybe it's the -ansi that got me.) If you change that line to 'Consumable Test1(Value);' then it should work. My other questions/comments still stand, however. Thanks.
ta0kira

PS I am without GCC right now.

Last edited by ta0kira; 04-13-2007 at 01:06 PM.
 
Old 04-13-2007, 11:08 AM   #6
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
That was my reasoning for thinking it should not compile

Quote:
My second question is what is your opinion of an object which is consumed by a function it is used in?
You mean just like an auto_ptr ?
Quote:
In a library I'm building I use the pattern above using single-use objects that are consumed when passed to a function. This is a memory management measure: certain functions take/return a consumable object to allow the user to modify the structure of dynamic objects.
Consumable object have the requirement that they need to be passed by reference then yes?
Quote:
The idea of the object is that a user can store one temporarily while they figure out where to put it, but if their temp goes out of scope then the enclosed pointer is deleted if the object hasn't been consumed by another function.
Again just like an auto pointer

Quote:
In other words, a function returns a pointer to the user under the condition they return it to something "responsible". If they don't then that pointer will be deleted, and if they don't even store the return then it is destroyed at the end of the call. Thanks.
Yes I can understand why you don't want to use an auto_ptr (think maybe because of differing STL versions?) but it provides all the functionality you are after. So maybe you should try emulating the same sort of behaviour that auto_ptr has (similar to the code I shown), It's a wrapper for a pointer so there should be a bool operator, pointer operator etc so to the outside world it looks and feels just like a pointer and allows a user to typedef it and never have to worry about how different it is to a pointer.

Last edited by dmail; 04-13-2007 at 11:09 AM.
 
Old 04-13-2007, 01:23 PM   #7
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by dmail
Yes I can understand why you don't want to use an auto_ptr (think maybe because of differing STL versions?) but it provides all the functionality you are after. So maybe you should try emulating the same sort of behaviour that auto_ptr has (similar to the code I shown), It's a wrapper for a pointer so there should be a bool operator, pointer operator etc so to the outside world it looks and feels just like a pointer and allows a user to typedef it and never have to worry about how different it is to a pointer.
I've already got the bool, !, ->, and * implemented. It intentionally won't be transparent except for those functions, however (i.e. conversion-to-pointer operator and * return const) because they aren't supposed to use it as a pointer; just hold on to it long enough to place it somewhere else. The smart pointer will hold a node removed from a tree and allow the user to hang on to it while deciding where to reinsert it and is by no means meant to give them access to a real pointer (appropriate functions return a real pointer.) I'm going completely non-template here so I need to use an lvalue operator instead of returning a proxy class like auto_ptr. The main reason for not using auto_ptr is the assignment operators, its templated constructors, and a check I need to make in the destructor before deleting the pointer.
Quote:
Originally Posted by dmail
Consumable object have the requirement that they need to be passed by reference then yes?
The mutating copy constructor handles that (but requires the lvalue conversion operator when embedding function calls), but I'm thinking about changing to references just for clarity. That will cut down on a few copies, but isn't required.
ta0kira
 
Old 10-22-2008, 11:28 PM   #8
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
It turns out the lvalue operator isn't actually used by stricter compilers (such as gcc 4.2.4 in Slackware 12.1-current,) so I just converted to a reference counter, anyway! It made me remember this discussion. The public interface only allows access to the const pointer and a friend class has access to the non-const version so that it can store it. The const-only public access prevents the developer from accidentally using the same pointer with two separate groups of reference-counting pointers. Before I was afraid that a smart pointer would go out of scope too soon, but it isn't a problem with the current library version. Thanks for the help, although very late!
ta0kira
 
Old 10-23-2008, 10:27 PM   #9
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
Nevermind! It wasn't quite that simple. In multi-threaded applications, it's possible for a reference counter to hold a pointer to an object and have the object be deleted independently. Sort of just talking to myself, but I'm just giving an update.
ta0kira

The solution seems to be reference-counting smart pointers plus linked lists of the smart pointers. It's all very complex, but I guess that's the price of memory management!

Last edited by ta0kira; 10-23-2008 at 10:57 PM.
 
  


Reply

Tags
c++, constructor, conversion, copy, pointer, smart


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Error: invalid lvalue in assignment xxrsc Linux - Software 1 08-17-2006 02:43 PM
Objects in C?? kickzha Programming 6 06-17-2006 09:38 PM
cache all objects xnet_online Linux - Software 1 04-28-2006 02:09 PM
java objects zaicheke Programming 3 03-23-2005 07:21 AM
wait_event_interruptible - invalid lvalue in unary `&' 7.e.Q Programming 4 11-08-2004 03:00 AM


All times are GMT -5. The time now is 06:31 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration