Problem with home-made auto_array!

Started by
34 comments, last by Rydinare 15 years, 2 months ago
Is there any good auto_array implementation available? I tried to write the home-made version given in the code below, but unfortunately, it doesn't work. The code
std::auto_array<int> foo() { return std::auto_array<int>(new int[2]); }
doesn't compile in g++. The following error is given: no matching function for call to ‘std::auto_array<int>::auto_array(std::auto_array<int>)’ candidates are: std::auto_array<X>::auto_array(std::auto_array<X>&) [with X = int] Here's the code:

namespace std {
	template <class X>
	class auto_array {
	public:
		explicit auto_array(X* p=NULL) throw();
		auto_array(auto_array& a) throw();
		~auto_array() throw();

		auto_array& operator=(auto_array& a) throw();

		X& operator[](int i) throw();
		const X& operator[](int i) const throw();

		X *get() const throw();
		X* release() throw();
		void reset(X* p=0) throw();

	private:
		X *m;
	};
}


namespace std {
	template <class X>
	auto_array<X>::auto_array(X* p) throw() : m(p) {}
	template <class X>
	auto_array<X>::auto_array(auto_array& a) throw() : m(a.m) { a.m=NULL; }
	template <class X>
	auto_array<X>::~auto_array() throw() { delete[] m; }

	template <class X>
	auto_array<X>& auto_array<X>::operator=(auto_array<X>& a) throw() {
		if(m!=a.m) {
			delete[] m;
			m=a.m;
			a.m=NULL;
		}
		return *this;
	}

	template <class X>
	X& auto_array<X>::operator[](int i) throw() { return m; }
	template <class X>
	const X& auto_array<X>::operator[](int i) const throw() { return m; }

	template <class X>
	X *auto_array<X>::get() const throw() { return m; }

	template <class X>
	X* auto_array<X>::release() throw() { X *p = m; m=NULL; return p; }

	template <class X>
	void auto_array<X>::reset(X* p) throw() { assert(m!=p); delete[] m; m=p; }
}


Advertisement
In order to implement return from functions, auto_ptr<> uses a member type, auto_ptr_ref<>. You'll need to implement a constructor that takes an auto_array_ref<> and a conversion operator to auto_array_ref<>. See your auto_ptr implementation for details of what that involves.
Bjarne Stroustrup says:

Quote:http://www.research.att.com/~bs/bs_faq2.html#auto_ptr
So should we use an auto_array to hold arrays? No. There is no auto_array. The reason is that there isn't a need for one. A better solution is to use a vector:


However, there is always Boost Smart Pointers, in which there is a smart pointer for dynamic arrays.

Also, don't define things in the std namespace.

[edit]

ninja'd++;
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Quote:Original post by all_names_taken
Is there any good auto_array implementation available?

You could use boost::shader_array for that.

Quote:The following error is given:

That's because copy constructor should have const argument:
auto_array(const auto_array& a) throw();


And don't put user made classes in std namespace. It is bad practice.
Quote:Original post by SiCrane
In order to implement return from functions, auto_ptr<> uses a member type, auto_ptr_ref<>. You'll need to implement a constructor that takes an auto_array_ref<> and a conversion operator to auto_array_ref<>. See your auto_ptr implementation for details of what that involves.

Ok, thanks!

...

Quote:bubu LV
You could use boost::shader_array for that.


Quote:Bjarne Stroustrup
So should we use an auto_array to hold arrays? No. There is no auto_array. The reason is that there isn't a need for one. A better solution is to use a vector:

I disagree.

First of all, auto_array gives a documentation advantage. A shared_array doesn't prohibit sharing, whereas auto_array simply says: this will only have one owner at the time.

Additionally, it allows for writing of more general and reusable library code. Example:
std::auto_array<Foo> foo();
This methods needs no knowledge whatsoever about what smart_ptr the user will put the returned array in. Thus, std::auto_array<Foo> foo() is both clean, generic and exception safe. Compare with:
Foo *foo();
This requires more effort to get exception safe, because the "Foo *" may be allocated some place inside the method, the data inside the array modified for a while, and the array finally returned. Or, the returned value may be sent directly as actual parameter to a constructor, only that an exception is thrown when some earlier member in the initialization list of that constructor is initialized. Another alternative:
boost::shared_array<Foo> foo();
This is clean and exception safe, but not general. Now all users must use a shared_array. They can't choose to assign the object to a boost::scoped_array, for example, or any other memory management class of their choice. Another alternative:
std::vector<Foo> foo();
Is again not generic. We are unable to later decide to use the allocated memory as a shared_array, unless we wrap yet another shared_ptr on top of it. But then, we not only get unnecessary overhead for scoped_array cases, but we also force all our client objects to change their interfaces to use shared instead of scoped, etc.

In short: without an std::auto_array, there's no way to make such a method simultaneously clean, exception safe and general in terms of what memory management strategy the client can choose to give to the returned pointer.
auto_ptr is dangerous and deprecated.
Don't use it or code something similar.
Quote:Original post by all_names_taken
*snip*
Usually you don't want to transfer responsibility like that. If I called foo() and got a Foo* array that I'd have to delete myself, I wouldn't be happy. Your API created that Foo* array, so in my view your API should be responsible for destroying that Foo* array (possibly by calling some Bar(Foo*) method).

You can, of course, roll your own auto_array class, but please, please don't put it in the std namespace. To be honest though I'm all out of suggestions, so I wish you luck.

Quote:Original post by loufoque
auto_ptr is dangerous and deprecated.
Don't use it or code something similar.
Now what makes you say that? I think the C++ International Standard begs to differ. See section 20.4.5
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Quote:Original post by MikeTacular
You can, of course, roll your own auto_array class, but please, please don't put it in the std namespace. To be honest though I'm all out of suggestions, so I wish you luck.

I think it works now, I did like SiCrane said and copied the auto_ptr_ref pattern from auto_ptr. Not yet sure how that stuff with auto_ptr_ref works, but it passed some very simple (and far from complete) unit tests and seems to work ok so far...

Here's the code if anyone is interested. The differences between this auto_array and auto_ptr are primarily:
- removal of conversion to/from other pointer types (not appropriate for arrays) as well as dereferencing (* and -> operators).
- addition of index operators
- using delete[] instead of delete everywhere
Otherwise it's just like auto_ptr.
#include <memory>	template<typename T>	struct auto_array_ref {		explicit auto_array_ref(T *m): m(m) {}		T *m;	};	template <class T>	class auto_array {	public:		explicit auto_array(T *p=NULL) throw();		auto_array(auto_array& a) throw();		auto_array(auto_array_ref<T> r) throw();		~auto_array() throw();		auto_array& operator=(auto_array& a) throw();		auto_array& operator=(auto_array_ref<T> r) throw();		T& operator[](int i) throw();		const T& operator[](int i) const throw();		T *get() const throw();		T *release() throw();		void reset(T *p=0) throw();		operator auto_array_ref<T>() throw();	private:		T *m;	};	template <class T>	auto_array<T>::auto_array(T* p) throw() : m(p) {}	template <class T>	auto_array<T>::auto_array(auto_array& a) throw() : m(a.m) { a.m=NULL; }	template <class T>	auto_array<T>::auto_array(auto_array_ref<T> r) throw() : m(r.m) {}	template <class T>	auto_array<T>::~auto_array() throw() { delete[] m; }	template <class T>	auto_array<T>& auto_array<T>::operator=(auto_array<T>& a) throw() {		if(m!=a.m) {			delete[] m;			m=a.m;			a.m=NULL;		}		return *this;	}	template <class T>	auto_array<T>& auto_array<T>::operator=(auto_array_ref<T> r) throw() {		if (r.m != m) {			delete m;			m = r.m;		}		return *this;	}	template <class T>	T& auto_array<T>::operator[](int i) throw() { return m; }	template <class T>	const T& auto_array<T>::operator[](int i) const throw() { return m; }	template <class T>	T *auto_array<T>::get() const throw() { return m; }	template <class T>	T* auto_array<T>::release() throw() { T *p = m; m=NULL; return p; }	template <class T>	void auto_array<T>::reset(T* p) throw() { assert(m!=p); delete[] m; m=p; }	template<class T>	auto_array<T>::operator auto_array_ref<T>() throw() {		return auto_array_ref<T>(release());	}
Quote:Original post by MikeTacular
Quote:Original post by loufoque
auto_ptr is dangerous and deprecated.
Don't use it or code something similar.
Now what makes you say that? I think the C++ International Standard begs to differ. See section 20.4.5


You have to understand something to make sense of loufoque's posts: loufoque posts from the future. He lives in a world where the C++0x standard already exists and is implemented on compilers, so he offers advice from a perspective that is incompatible with real world of early 2009 that most of us are currently living in. In the future universe that loufoque posts from, std::auto_ptr has been replaced by unique_ptr. However, the unique_ptr implementation relies on rvalue references in C++0x, which in the real world lacks compiler support. So, whenever you see one of loufoque's sentences that doesn't make sense, like "auto_ptr is dangerous and deprecated" add "In the year 2014" to the front of it. So the sentence becomes "In the year 2014, auto_ptr is dangerous and deprecated."
Quote:Original post by SiCrane
Quote:Original post by MikeTacular
Quote:Original post by loufoque
auto_ptr is dangerous and deprecated.
Don't use it or code something similar.
Now what makes you say that? I think the C++ International Standard begs to differ. See section 20.4.5


You have to understand something to make sense of loufoque's posts: loufoque posts from the future. He lives in a world where the C++0x standard already exists and is implemented on compilers, so he offers advice from a perspective that is incompatible with real world of early 2009 that most of us are currently living in. In the future universe that loufoque posts from, std::auto_ptr has been replaced by unique_ptr. However, the unique_ptr implementation relies on rvalue references in C++0x, which in the real world lacks compiler support. So, whenever you see one of loufoque's sentences that doesn't make sense, like "auto_ptr is dangerous and deprecated" add "In the year 2014" to the front of it. So the sentence becomes "In the year 2014, auto_ptr is dangerous and deprecated."


Wow, I didn't realize he was from the future. I wish I could jump there and start programming in C++0x. Adding "In the year 2014" certainly helps clarify it all, thanks SiCrane!
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

This topic is closed to new replies.

Advertisement