'new' class method

Bill Baxter wbaxter at gmail.com
Thu Oct 23 14:26:57 PDT 2008


On Fri, Oct 24, 2008 at 5:44 AM, bearophile <bearophileHUGS at lycos.com> wrote:
> Bill Baxter:
>> How about this as a start:
>>
>> // Default cases
>> auto a = SomeStruct(arg);  // struct on the stack
>> auto a = SomeClass(arg);  // class on the heap
>>
>> // Anti-default cases
>> auto a = SomeStruct.new(arg);  // struct on the heap
>> scope a = SomeClass(arg);  // class on the stack
>>
>> // Placement cases
>> ...
>
> I agree that the placement new syntax can be less nice.
>
> But for the struct/class cases I'd like something more symmetric :-)
> A first silly possibility:
>
> auto a = SomeStruct(arg); // stack
> auto a = SomeStruct.new(arg); // heap
> auto a = SomeStruct.pnew(placement_arg)(arg); // placement
> A possible alternative:
> auto a = SomeStruct.pnew(placement_arg).new(arg); // placement
>
> auto a = SomeClass(arg); // stack
> auto a = SomeClass.new(arg); // heap
> auto a = SomeClass.pnew(placement_arg)(arg); // placement
> A possible alternative:
> auto a = SomeClass.pnew(placement_arg).new(arg); // placement

Well, D tries to make heap classes look syntactically the same stack
structs in most cases despite the semantic differences.   "Foo x" is
either a stack struct or a heap class.   The 'x' does not look like a
pointer if it's a class, but in fact it is a pointer.  That was D's
big change over C++ -- make the *default* case for those two use the
same syntax.

So I don't see why construction should be the only place to put that
distinction in your face.  The default syntaxes look the same
elsewhere, so the default scheme for construction should look the same
for both.

Such uniformity is also going to be better for writing templates that
construct things.  Structs are more likely to be pure value types than
classes.  So if I want to write a template that does non-default
construction of an array of Things I'd rather be able to write this:

Thing[] make_thing_array(Thing, ConstructArgs...)(size_t len, ConstructArgs A)
{
       Thing[]  ret;  ret.length = len;
       foreach(ref o; ret) {  o = Thing(A); }
}

Than have to resort to static ifs all the time just to get the default behavior:

Thing[] make_thing_array(Thing, ConstructArgs...)(size_t len, ConstructArgs A)
{
       Thing[]  ret;  ret.length = len;
       static if(is(Thing : class)) {
          foreach(ref o; ret) {  o = new Thing(A); }
       }
       else {
          foreach(ref o; ret) {  o = Thing(A); }
       }
}

Put another way, I think the case of "give me default allocation for
the type" is more common in generic code than "give me heap allocation
no matter what".

So for me, the main thing I care about is making the *default*
allocation strategy use the same syntax.  Beyond that I don't really
care much what syntax is chosen.

... But here's another thought.  It doesn't really have to be me vs
you here.  Both cases do have some use.  Maybe the best thing here is
a syntax that can *either* emphasize sameness of allocation *or*
emphasize default behavior.

So then:
auto a = Class();  // heap   (default)
auto a = Struct(); // stack   (default)

auto a = Class.new(stack)(); // stack
auto a = Struct.new(stack)(); // stack

auto a = Class.new(heap)(); // heap
auto a = Struct.new(heap)(); // heap

void *addr;
auto a = Class.new(addr)(); // placement
auto a = Struct.new(addr)(); // placement

One thing to consider is that anything other than the default strategy
is really a kind of a vague placement new.  You're telling the
compiler to construct the object in some non-default memory location,
you're just not telling it exactly what the address is, just whether
the address should be in heap or stack.

In the above "heap" and "stack" are context keywords of new.

--bb



More information about the Digitalmars-d mailing list