Decision on container design

Denis Koroskin 2korden at gmail.com
Fri Jan 28 17:10:06 PST 2011


On Sat, 29 Jan 2011 02:32:28 +0300, Michel Fortin  
<michel.fortin at michelf.com> wrote:

> On 2011-01-28 17:09:08 -0500, Andrei Alexandrescu  
> <SeeWebsiteForEmail at erdani.org> said:
>
>> On 1/28/11 3:05 PM, Michel Fortin wrote:
>>> Not my preferred choices (especially #1), but having containers in
>>> Phobos will certainly be an improvement over not having them. So go  
>>> ahead!
>>  Well if you brought forth some strong argument I'm all ears. What I  
>> see for now is that struct containers are just difficult to implement  
>> and need to have carefully explained semantics, whereas a lot of people  
>> know how classes behave from day one.
>
> We already argument this over and over in the past. First, I totally  
> acknowledge that C++ style containers have a problem: they make it  
> easier to copy the content than pass it by reference. On the other side  
> of the spectrum, I think that class semantics makes it too easy to have  
> null dereferences, it's easy to get lost when you have a container of  
> containers.
>
> I have some experience with containers having class-style semantics: in  
> Objective-C, I ended up creating a set of macro-like functions which I  
> use to initialize containers whenever I use them in case they are null.  
> And I had to do more of these utility functions to handle a particular  
> data structure of mine which is a dictionary of arrays of objects. In  
> C++, I'd have declared this as a "map< string, vector< Object > >" and  
> be done with it; no need for special care initializing each vector, so  
> much easier than in Objective-C.
>
> I agree that defining structs to have reference semantics as you have  
> done is complicated. But I like the lazy initialization, and we have a  
> precedent for that with AAs (ideally, AAs would be a compatible  
> container too). Can't we just use the GC instead of reference counting?  
> I'd make things much easier. Here is a implementation:
>
> 	struct Container
> 	{
> 		struct Impl { ... }
>
> 		private Impl* _impl;
> 		ref Impl impl() @property
> 		{
> 			if (!impl) impl = new Impl;
> 			return *impl;
> 		}
> 	
> 		alias impl this;
> 	}
>

Unfortunately, this design has big issues:


void fill(Appender appender)
{
     appender.put("hello");
     appender.put("world");
}

void test()
{
     Appender<string> appender;
     fill(appender); // Appender is supposed to have reference semantics
     assert(appender.length != 0); // fails!
}

Asserting above fails because at the time you pass appender object to the  
fill method it isn't initialized yet (lazy initialization). As such, a  
null is passed, creating an instance at first appending, but the result  
isn't seen to the caller.

An explicit initialization is needed to work around this design issue. The  
worst thing is that in many cases it would work fine (you might have  
already initialized it indirectly) but sometimes you get unexpected  
result. I got hit by this in past, and it wasn't easy to trace down.

As such, I strongly believe containers either need to have copy semantics,  
or be classes. However, copy semantics contradicts with the "cheap copy  
ctor" idiom because you need to copy all the elements from source  
container.

> I also believe reference semantics are not to be used everywhere, even  
> though they're good most of the time. I'd like to have a way to bypass  
> it and get a value-semantic container. With the above, it's easy as long  
> as you keep Container.Impl public:
>
> 	void main() {
> 		Container      lazyHeapAllocatedContainer;
> 		Container.Impl stackAllocatedContainer;
> 	}
>
> 	void MyObject {
> 		Container.Impl listOfObjects;
> 	}
>
>
>>> About #4, it'd be nice to have the containers use move semantics when
>>> possible even if they fallback to (cheap) copy semantic when move isn't
>>> available. That way, if you have a type which is moveable but not
>>> copyable you can still put it in a container. Does that makes sense?
>>  That's what I did up until now. It is tantamount to defining a bunch  
>> of methods (aliases or not) that add to the interface that the user  
>> must absorb, but that are seldom useful. It just seems that the entire  
>> move paraphernalia doesn't lift its weight.
>
> But could we limit this to say that only containers that can return  
> elements by ref? Perhaps that won't help. You know the problem better  
> than me, I don't really have anything more to say.
>
>


-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/


More information about the Digitalmars-d mailing list