Sign in to follow this  
all_names_taken

Problem with home-made auto_array!

Recommended Posts

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[i]; }
	template <class X>
	const X& auto_array<X>::operator[](int i) const throw() { return m[i]; }

	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; }
}


Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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++;

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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[i]; }

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


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());
}



Share this post


Link to post
Share on other sites
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."

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
In fairness, auto_ptr is dangerous because transfer of ownership is almost never what you want, in my experience. I actually make it habit to go through any code that uses auto_ptr and change it to scoped_ptr, instead. If I get a compilation error, then it is generally meant to be shared_ptr.

Share this post


Link to post
Share on other sites
Funny, every time I use std::auto_ptr, transfer of ownership is exactly what I want. Ex:

auto_ptr<Foo> get_foo(void);

This tells me that get_foo() returns a brand new Foo object just for me. On the other hand:

shared_ptr<Foo> get_foo(void);

doesn't have the same semantics. get_foo() could return a new Foo object, or it could return a reference to an already created Foo.

Admittedly, factory functions are pretty much the only place where I use auto_ptr.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Funny, every time I use std::auto_ptr, transfer of ownership is exactly what I want. Ex:

auto_ptr<Foo> get_foo(void);

This tells me that get_foo() returns a brand new Foo object just for me. On the other hand:

shared_ptr<Foo> get_foo(void);

doesn't have the same semantics. get_foo() could return a new Foo object, or it could return a reference to an already created Foo.

Admittedly, factory functions are pretty much the only place where I use auto_ptr.


Ahh, this is a difference in style. I use neither of those for factories. I return a raw pointer from the factory, since having the memory managed isn't useful at that point and therefore I'm imposing no restrictions on the caller for how they manage the memory, as to whether they want to use shared_ptr or scoped_ptr.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rydinare
Quote:
Original post by SiCrane
Funny, every time I use std::auto_ptr, transfer of ownership is exactly what I want. Ex:

auto_ptr<Foo> get_foo(void);

This tells me that get_foo() returns a brand new Foo object just for me. On the other hand:

shared_ptr<Foo> get_foo(void);

doesn't have the same semantics. get_foo() could return a new Foo object, or it could return a reference to an already created Foo.

Admittedly, factory functions are pretty much the only place where I use auto_ptr.


Ahh, this is a difference in style. I use neither of those for factories. I return a raw pointer from the factory, since having the memory managed isn't useful at that point and therefore I'm imposing no restrictions on the caller for how they manage the memory, as to whether they want to use shared_ptr or scoped_ptr.


But if you want to follow RAII then returning a classed resource is a lot more in keeping with that idoim. All those classed resources allow access to the underlying resource so you are not exactly limiting the client in anyway, slight overhead for robust code which is hardly a loss.

I'm just after reading some of Scott Meyers 55, and it mentioned lots of whats in this thread; just to highlight the worth to me :p, including the 'never place an implementation in std namespace, its not allowed and you will be punished :p', standard template 'complete' specialisations only.

Share this post


Link to post
Share on other sites
Quote:
Original post by Guthur
Quote:
Original post by Rydinare
Quote:
Original post by SiCrane
Funny, every time I use std::auto_ptr, transfer of ownership is exactly what I want. Ex:

auto_ptr<Foo> get_foo(void);

This tells me that get_foo() returns a brand new Foo object just for me. On the other hand:

shared_ptr<Foo> get_foo(void);

doesn't have the same semantics. get_foo() could return a new Foo object, or it could return a reference to an already created Foo.

Admittedly, factory functions are pretty much the only place where I use auto_ptr.


Ahh, this is a difference in style. I use neither of those for factories. I return a raw pointer from the factory, since having the memory managed isn't useful at that point and therefore I'm imposing no restrictions on the caller for how they manage the memory, as to whether they want to use shared_ptr or scoped_ptr.


But if you want to follow RAII then returning a classed resource is a lot more in keeping with that idoim. All those classed resources allow access to the underlying resource so you are not exactly limiting the client in anyway, slight overhead for robust code which is hardly a loss.


I guess that's fair. My concern is that someone who isn't familiar enough with auto_ptr will think it can be used like a regular "smart" pointer, when it really has different semantics, and this will cause some difficult to find bugs. As to where, I think the factory pattern is well understood to return dynamic objects, so there's no surprises. If there was something better to return than auto_ptr<>, I'd feel a little better about this suggestion.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rydinare
Ahh, this is a difference in style. I use neither of those for factories. I return a raw pointer from the factory, since having the memory managed isn't useful at that point and therefore I'm imposing no restrictions on the caller for how they manage the memory, as to whether they want to use shared_ptr or scoped_ptr.


Returning a auto_ptr doesn't stop the client from dumping it in whatever smart pointer they want. There's a shared_ptr constructor and scoped_ptr constructor just for sinking auto_ptrs. That is to say, these are perfectly legal:

boost::shared_ptr<Foo> share_me(get_foo());
boost::scoped_ptr<Foo> dont_share_me(get_foo());


Share this post


Link to post
Share on other sites
I know where you are coming from; I think I would have fallen into the trap of thinking auto_ptr<>'s copying functionality is a copy as oppose to a...well move:p.

But I think I may actually use the differing functionality at some point. I'm thinking it would be useful if you have a singleton like object that you want to share and access exclusively; thread safety may be be one area it has a use; I will qualify that by saying I have absolutely no experience of multithread programming :)

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Quote:
Original post by Rydinare
Ahh, this is a difference in style. I use neither of those for factories. I return a raw pointer from the factory, since having the memory managed isn't useful at that point and therefore I'm imposing no restrictions on the caller for how they manage the memory, as to whether they want to use shared_ptr or scoped_ptr.


Returning a auto_ptr doesn't stop the client from dumping it in whatever smart pointer they want. There's a shared_ptr constructor and scoped_ptr constructor just for sinking auto_ptrs. That is to say, these are perfectly legal:

boost::shared_ptr<Foo> share_me(get_foo());
boost::scoped_ptr<Foo> dont_share_me(get_foo());


*scratching chin* O I C. Hmmm. I hadn't realized they were there, because I generally have just avoided auto_ptrs. You have me rethinking that a bit... but I still have the concern that someone unfamiliar will make the mistake. I can speak from experience, because I used auto_ptrs incorrectly for a while when I first started using STL, many years ago. A lot of the time I got lucky and didn't get burned, but every once in a while I came across a bug that I couldn't solve and would wind up rewriting a portion of code until it worked. All because of the name auto, when you're new, you assume auto is short for "automatic", which makes you say, "Ah, sweet. They take care of everything!"

To rephrase, my concern is someone will do this:


std::auto_ptr<Foo> share_me(get_foo());


But, I definitely see where you're coming from, and I may start using your technique, despite the newbie risk. I'll just have to make sure the junior programmers I work with are aware.

Share this post


Link to post
Share on other sites
If the only reason you want an auto_ptr (or auto_array) is so you can create the object in a factory function and return it, wouldn't it be possible to make the auto_array non-copyable and use the release method to return the naked pointer? This should still follow RAII principles?


X* foo()
{
auto_array<X> result(new X[n]);
//do some more stuff that might throw
//if it throws, result will be cleaned up
//...
//success
return result.release();
}

//store result in another auto_array if you so wish:
auto_array<X> some_x(foo());


Share this post


Link to post
Share on other sites
Quote:
But, I definitely see where you're coming from, and I may start using your technique, despite the newbie risk. I'll just have to make sure the junior programmers I work with are aware.

Well, it's not as if auto_ptr is a hidden corner of the standard library. Within arms reach right now I've got five C++ books (six if you count the standard). Four of them (The C++ Standard Library by Josuttis, Effective STL by Meyers, C++ Coding Standards by Sutter and Alexandrescu and C++ Gotchas by Dewhurst) contain warnings about auto_ptr. So statistically, I would expect any C++ programmer who's read two or more books on C++ to have some idea as to how auto_ptr works. I don't think that that would be considered an unreasonable expectation for a professional programmer or even a serious hobbiest using C++.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Quote:
But, I definitely see where you're coming from, and I may start using your technique, despite the newbie risk. I'll just have to make sure the junior programmers I work with are aware.

Well, it's not as if auto_ptr is a hidden corner of the standard library. Within arms reach right now I've got five C++ books (six if you count the standard). Four of them (The C++ Standard Library by Josuttis, Effective STL by Meyers, C++ Coding Standards by Sutter and Alexandrescu and C++ Gotchas by Dewhurst) contain warnings about auto_ptr. So statistically, I would expect any C++ programmer who's read two or more books on C++ to have some idea as to how auto_ptr works. I don't think that that would be considered an unreasonable expectation for a professional programmer or even a serious hobbiest using C++.


No disagreement on my end, as I've read all of those. Nonetheless, I would say that the majority of programmers that I've worked with don't read those books and take a lot of prodding to start opening them. I agree that it's certainly not unreasonable, but unfortunately I don't expect it, as the team was put into place before I arrived and I would estimate that at least 80% of C++ programmers I've worked with professionally over the years wouldn't have heard of auto_ptr.

But, yes, I agree. Coddling around basic STL features is probably not a good policy, so I'm coming around to the use of auto_ptr in the manner you described.

Share this post


Link to post
Share on other sites
I think one reason might have been for length of time it took STL to actually become widely accept as better than to 'roll your own' containers and all.

You only have to read it once, like I did today, and you know, it's not really a big deal. Unlike flatpack furniture you should always read the instructions when it comes to programming :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Guthur
I think one reason might have been for length of time it took STL to actually become widely accept as better than to 'roll your own' containers and all.

You only have to read it once, like I did today, and you know, it's not really a big deal. Unlike flatpack furniture you should always read the instructions when it comes to programming :)


No doubt. Unfortunately a lot of programmers, especially inexperienced ones, are simply hackers... and it takes a long time to grow out of it, if ever.

Share this post


Link to post
Share on other sites
Sorry to slightly hijack this;

Is there a reason why an auto_ptr to a vector would be bad, I'm not sure if its really applicable here but I'm curious, all references I find to the subject is vector of auto_ptr.

Share this post


Link to post
Share on other sites
I can't see a problem with:

auto_ptr< vector<Foo> > GetSequenceOfFoo()

other than the usual caveats with auto_ptr. auto_ptr's don't work inside the vecotr because their transfer of ownership semantics aren't compatible with the copying of members that vector is free to do.

I hadn't thought about auto_ptr as a natural fit for factory functions before now. But it makes so much sense (unless you're using a fixed pool inside the factory, but that's a special case). I'm adding it to the mental toolbox. Thanks SiCrane!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this