Is mimicking a reference type with a struct reliable?

Steven Schveighoffer schveiguy at yahoo.com
Sat Oct 16 10:29:04 PDT 2010


On Sat, 16 Oct 2010 12:59:46 -0400, Denis Koroskin <2korden at gmail.com>  
wrote:

> Sorry, I misclicked a button and send the message preliminary.
>
> On Sat, 16 Oct 2010 20:16:40 +0400, Steven Schveighoffer  
> <schveiguy at yahoo.com> wrote:
>>
>> A final option is to disable the copy constructor of such an unsafe  
>> appender, but then you couldn't pass it around.
>>
>> What do you think?  If you think it's worth having, suggest it on the  
>> phobos mailing list, and we'll discuss.
>>
>
> It's still possible to pass it by reference, or even by pointer. You  
> know, that's what you actually do right now - you are passing a Data* (a  
> pointer to an internal state, wrapped with an Appender struct).

Yes, doing it this way forces you to use a pointer, since you can't pass  
by value.  That is the point.  To create a type with the property "if you  
don't pass it around correctly, it might blow up in your face" doesn't  
make much sense.  This is why I'd recommend using class for Appender,  
which also forces reference semantics, but does not use lazy construction.

>> Note that Appender is supposed to be fast at *appending* not  
>> initializing itself.  In that respect, it's very fast.
>>
>
> This makes it useless for appending small amount of data.

Any generally usable appender is going to have some startup cost, so yes  
the overhead is going to make it non-optimal for small appends.  Use ~=  
for small amounts of data or use your method (writing directly to a  
buffer).  Appender is for appending large amounts of data.

>>
>> This is a point I've brought up before.  As of yet there is no  
>> solution.  There have been a couple of ideas passed around, but there  
>> hasn't been anything decided.  The one idea I remember (but didn't  
>> really like) is to have the copy constructor be able to modify the  
>> original.  This makes it possible to allocate the underlying  
>> implementation in Appender for example, even on the data being passed.   
>> There are lots of problems with this solution, and I don't think it got  
>> much traction.
>>
>> I think the default constructor solution is probably never going to  
>> happen.  It's very nice to always have a default fast way to initialize  
>> structs, and there is precedence (C# has the same rule).
>>
>
> I think there is, but it goes far beyond default ctors problem (it  
> solves many other issues, too).
> Currently, a struct is initialized with T.init/T.classinfo.init
> Pros:
> simple initialization - malloc, followed by memcpy
> there is always an immutable instance of an object in memory, and you  
> can use it as default/not initialized state
>
> Cons:
> you can't initialize class/struct variables with runtime values
> increased file size (every single class/struct now has a copy of its own)
>
> In Java, they use another approach. Instead of memcpy'ing T.init on top  
> of allocated data, they invoke a so-called cctor (as opposed to ctor).  
> This is a method that initializes memory so that a ctor can be called.  
> memcpy'ing T.init has the same idea, however it is not moved into a  
> separate method. In general, cctor can be implemented the way it is in D  
> without sacrificing anything. However, a type-unique method is a lot  
> better than that:
>
> 1) most structs initialize all of its members with 0. For these compiler  
> can use memset instead.
> 2) killer-feature in my opinion. It allows initializing values to  
> non-constant expressions:
>
> class Foo
> {
> 	ubyte[] buffer = new ubyte[BUFFER_SIZE];
> }
>
> This also solves an Appender issue:
>
> struct Appender
> {
> 	Data* data = new Data();
> }
>
> 3) it allows getting rid of T.init, significantly reducing resulting  
> file size
>
> I'm not sure Walter will agree to such a radical change, but it can be  
> achieved in small steps. D doesn't even have to get rid of T.init, it  
> can still be there (but I'd like to get rid of it eventually)
>
> a) Keep T.init/T.classinfo.init, introduce compiler-generated cctor what  
> memcpy'ies T.init over the object
> (Optionally) Make cctor more smart, and generate proper class/struct  
> initialization code that doesn't rely on T.init
> b) Allow non-constant expressions as initializers and initialize such  
> members in the cctor
> (Optionally) Get rid of T.init altogether

This does sound promising.  I think we would need to try and make the  
'cctor' in D be very simple (low cost) otherwise you'll see issues when  
you for example allocate an array of structs.

So for example, you might only allow memory allocation and assignment.   
That would probably be enough for most cases, and would be (hopefully)  
fast enough to be not-noticable.  Not only that, but since the compiler is  
in charge of creating the cctor, it might be able to do some  
optimizations, like if you are allocating an array of Appenders, it can  
bulk construct all the data members required (i.e. take the GC lock only  
once).

Andrei, Walter?

-Steve


More information about the Digitalmars-d mailing list