April 26, 2011
On 4/26/11, Daniel Gibson <metalcaedes@gmail.com> wrote:
> 2. you can even use different custom allocators for the same class at the same time (you'd have to be really careful not to screw that up by using the wrong deallocator for an Object)

Maybe the allocator could annotate the class somehow, and the deallocator could check which allocator was used to create the object so it can call the matching deallocator.

One way this could be done is through subclassing, but maybe that would mess things up. And this would probably mean runtime checks as well. I've never dealt with this before, but some ideas come to mind..
April 26, 2011
Am 26.04.2011 23:49, schrieb Andrej Mitrovic:
> On 4/26/11, Daniel Gibson <metalcaedes@gmail.com> wrote:
>> 2. you can even use different custom allocators for the same class at the same time (you'd have to be really careful not to screw that up by using the wrong deallocator for an Object)
> 
> Maybe the allocator could annotate the class somehow, and the deallocator could check which allocator was used to create the object so it can call the matching deallocator.
> 
> One way this could be done is through subclassing, but maybe that would mess things up. And this would probably mean runtime checks as well. I've never dealt with this before, but some ideas come to mind..

Yeah, just having one int/enum flag that indicates which allocator was
used would be sufficient.
However for that to work you have to use custom classes, that are all
derived from MyObject or something like that (which is derived from
Object and contains this flag). Depending on what you're doing this may
not be a problem at all, but it's not as generic as being able to use
*any* class.

(Maybe?) Another possibility: the allocator returns a struct/class that contains this flag + the object and uses alias this to redirect everything to the contained class? I haven't really thought this through, but something like this could work.

Cheers,
- Daniel
April 26, 2011
On 26.04.2011 23:43, Timon Gehr wrote:

> But you understand why it is deprecated for GC memory?

  Sure, I do. Though, sometimes it is useful to immediately deallocate an object, instead of delegating this to GC (and most likely - to delay deallocation). There was a post concerning thoughts of Linus Torvalds about GC, and the keyword is -
"cache". Another possible use - in case of large objects, which are used for short period of time - it makes little sense to keep them around until next GC cycle, as this may lead to another request to OS, which may be unnecessary otherwise.

> The main thing to note is that the semantics of C++ 'new' and D 'new' are rather different.

  From programmer's point of view - not really. 'new' will allocate (and create) an object, which is not really different from C++. Or?

> D 'new' performs allocation on the GC heap by default. The only sane overloads of 'new' would therefore allocate the object on a custom GC heap, which you never want to do.

  Why it is the only "sane"? Why I couldn't allocate from somewhere else? If D is positioned as language for system programming (not only, but anyway), it is quite OK to use other (de)allocators.

> The argument for removing 'delete' overloading is trivial after taking that into consideration.

  Still, to be honest, I don't see, what is the argument. 'delete' looks quite OK for explicit deallocation, regardless of where the object was allocated.

> You can still create custom allocators by the means of template functions. (I do not think this is optimal though because they duplicate code that needn't be)

 This way, we delegate all this to the developer, as he has to care - where objects should be allocated. OTOH, some objects (in specific environments) may "know better", where they should be allocated - that's why I believe that overloading makes sense.

> They feel a little bit less natural though, which is not a problem, since in D, custom allocators and manual memory management in general, _are_ less natural.

  Why? In some cases (again - system and real-time programming) - explicit (de)allocation is a must. Why not leave things as is? Those who don't need it will never use them, those who do need - will always have the option to use.

> You don't gain anything by overloadable new/delete in D, because you cannot use the feature to quickly patch in a custom allocator to existing code as possible in C++ as a means of optimization. (this binds that allocator to a specific type, which does not make much sense in other context)

  Exactly the point - for specific (derived) types, for specific application, when D Runtime/Phobos
  are not used (partially or fully) - this makes sense, IMHO.

/Alexander
April 26, 2011
On Wed, 27 Apr 2011 00:01:58 +0300, Daniel Gibson <metalcaedes@gmail.com> wrote:

> Am 26.04.2011 21:48, schrieb Benjamin Thaut:
>> Am 26.04.2011 21:26, schrieb Daniel Gibson:
>>> Am 26.04.2011 20:23, schrieb Steven Schveighoffer:
>>>> On Tue, 26 Apr 2011 14:14:11 -0400, Benjamin Thaut
>>>> <code@benjamin-thaut.de>  wrote:
>>>>> Could someone please write a small example how manual memory
>>>>> management would look without new / delete?
>>>>
>>>> I think that is a good idea.
>>>>
>>>
>>> Not a tutorial, just a simple example using C malloc/free:
>>> http://pastebin.com/HSBrk5kA
>>>
>>> Cheers,
>>> - Daniel
>>
>> Thanks for the example, thats exactly what I needed.
>>
>> I still don't understand why the delete operator is deprecated
>> completely. It could be defined, that it is only useable if the new and
>> delete operator have been overloaded in the class or struct that is
>> tried to be deleted.
>>
>
> This way it's much more flexible. Note that you don't have to change the
> classes at all (i.e. you don't have to provide new() and delete()) to
> use a custom allocator. This means
> 1. you can use custom allocators with classes you didn't write yourself
> 2. you can even use different custom allocators for the same class at
> the same time (you'd have to be really careful not to screw that up by
> using the wrong deallocator for an Object)
> 3. you only have to write an (de)allocator once and you can use it for
> any class, even if they're not derived from each other
>
> Also, and I think this is really a great feature for performance that
> isn't available with overloaded new and delete operators, you can create
> an array of objects that really lie next to each other in memory (like
> an array of structs), so you can benefit from CPU cache effects.
>
> This is a very simple example doing that: http://pastebin.com/98mGU7y1
> However you have to be careful with it if, i.e. don't put new objects in
> it, don't slice it (you have to at least feed the original array to
> myDeleteArr()) etc.
> To really use something like this probably a struct that enforces this
> (by conservatively overloading opIndex, opAssign etc) would be better
> than a function returning a dynamic array that happens to point to
> objects that are adjacent in memory.
>
> Cheers,
> - Daniel

Those that come from other languages (and D1), come with some baggage, it is strange not being able to see the huge advantages over new/delete operators. I agree with others, we need the best documentation we can get on this subject.

A challenge.
Write a "new" wrapper (lets call it "mynew") that logs each allocation (without preprocessor magic and C++0x).

D:
T mynew(T, A...)(A a) {
	printf("hello");
//	return new T(a);
	return new!T(a);
}

Any takers for the C++ version?

Daniel, you could have named them new and delete then everyone would be happy!
April 26, 2011
Am 27.04.2011 00:31, schrieb so:
> On Wed, 27 Apr 2011 00:01:58 +0300, Daniel Gibson <metalcaedes@gmail.com> wrote:
> 
>> Am 26.04.2011 21:48, schrieb Benjamin Thaut:
>>> Am 26.04.2011 21:26, schrieb Daniel Gibson:
>>>> Am 26.04.2011 20:23, schrieb Steven Schveighoffer:
>>>>> On Tue, 26 Apr 2011 14:14:11 -0400, Benjamin Thaut <code@benjamin-thaut.de>  wrote:
>>>>>> Could someone please write a small example how manual memory management would look without new / delete?
>>>>>
>>>>> I think that is a good idea.
>>>>>
>>>>
>>>> Not a tutorial, just a simple example using C malloc/free: http://pastebin.com/HSBrk5kA
>>>>
>>>> Cheers,
>>>> - Daniel
>>>
>>> Thanks for the example, thats exactly what I needed.
>>>
>>> I still don't understand why the delete operator is deprecated completely. It could be defined, that it is only useable if the new and delete operator have been overloaded in the class or struct that is tried to be deleted.
>>>
>>
>> This way it's much more flexible. Note that you don't have to change the
>> classes at all (i.e. you don't have to provide new() and delete()) to
>> use a custom allocator. This means
>> 1. you can use custom allocators with classes you didn't write yourself
>> 2. you can even use different custom allocators for the same class at
>> the same time (you'd have to be really careful not to screw that up by
>> using the wrong deallocator for an Object)
>> 3. you only have to write an (de)allocator once and you can use it for
>> any class, even if they're not derived from each other
>>
>> Also, and I think this is really a great feature for performance that isn't available with overloaded new and delete operators, you can create an array of objects that really lie next to each other in memory (like an array of structs), so you can benefit from CPU cache effects.
>>
>> This is a very simple example doing that: http://pastebin.com/98mGU7y1
>> However you have to be careful with it if, i.e. don't put new objects in
>> it, don't slice it (you have to at least feed the original array to
>> myDeleteArr()) etc.
>> To really use something like this probably a struct that enforces this
>> (by conservatively overloading opIndex, opAssign etc) would be better
>> than a function returning a dynamic array that happens to point to
>> objects that are adjacent in memory.
>>
>> Cheers,
>> - Daniel
> 
> Those that come from other languages (and D1), come with some baggage, it is strange not being able to see the huge advantages over new/delete operators. I agree with others, we need the best documentation we can get on this subject.
> 
> A challenge.
> Write a "new" wrapper (lets call it "mynew") that logs each allocation
> (without preprocessor magic and C++0x).
> 
> D:
> T mynew(T, A...)(A a) {
>     printf("hello");
> //    return new T(a);
>     return new!T(a);
> }
> 
> Any takers for the C++ version?
> 
> Daniel, you could have named them new and delete then everyone would be happy!

I could, but dmd wouldn't compile that because new and delete are
reserved keywords ;)
And by the way, your code won't compile because new!T(a) is illegal -
new isn't a template. Or is that with my hypothetical new template that
doesn't compiler either?

Cheers,
- Daniel
April 26, 2011
so:

> I agree with others, we need the best documentation we can get on this subject.

In-place allocation (or generally other forms of manual allocation and deallocation) of class instances in D2 is a topic well fit for a *long* article for the iPad2 contest (written for future D programmers coming from C++ too), and later this good article has to go among the D docs of the official documentation :-)

Bye,
bearophile
April 26, 2011
> Or is that with my hypothetical new template that
> doesn't compiler either?

Yes, and the commented one is for the current solution.
April 26, 2011
Am 27.04.2011 00:58, schrieb bearophile:
> so:
> 
>> I agree with others, we need the best documentation we can get on this subject.
> 
> In-place allocation (or generally other forms of manual allocation and deallocation) of class instances in D2 is a topic well fit for a *long* article for the iPad2 contest (written for future D programmers coming from C++ too), and later this good article has to go among the D docs of the official documentation :-)
> 
> Bye,
> bearophile

The D2 documentation should stop advertising custom allocators via new()
and delete() and the scope storage class.
This means http://www.digitalmars.com/d/2.0/memory.html and
http://www.digitalmars.com/d/2.0/comparison.html (possibly more) should
be rewritten or adjusted.
BTW the comparison also lists strong typedefs which are also to be
deprecated AFAIK.

Furthermore: Why is emplace() in std.conv? I really wouldn't expect it
there.. more likely I'd look in std.typecons, because scoped!T() is
already there and does something related (allocate an object without new).

And while I'm complaining: http://www.digitalmars.com/d/2.0/phobos/phobos.html doesn't list core.stdc.*

Cheers,
- Daniel
April 26, 2011
Am 27.04.2011 01:22, schrieb Daniel Gibson:
> Am 27.04.2011 00:58, schrieb bearophile:
>> so:
>>
>>> I agree with others, we need the best documentation we can get on this subject.
>>
>> In-place allocation (or generally other forms of manual allocation and deallocation) of class instances in D2 is a topic well fit for a *long* article for the iPad2 contest (written for future D programmers coming from C++ too), and later this good article has to go among the D docs of the official documentation :-)
>>
>> Bye,
>> bearophile
> 
> The D2 documentation should stop advertising custom allocators via new()
> and delete() and the scope storage class.
> This means http://www.digitalmars.com/d/2.0/memory.html and
> http://www.digitalmars.com/d/2.0/comparison.html (possibly more) should
> be rewritten or adjusted.
> BTW the comparison also lists strong typedefs which are also to be
> deprecated AFAIK.
> 
> Furthermore: Why is emplace() in std.conv? I really wouldn't expect it
> there.. more likely I'd look in std.typecons, because scoped!T() is
> already there and does something related (allocate an object without new).
> 
> And while I'm complaining: http://www.digitalmars.com/d/2.0/phobos/phobos.html doesn't list core.stdc.*
> 
> Cheers,
> - Daniel

Oh and http://www.digitalmars.com/d/2.0/phobos/core_memory.html should
also be adjusted in GC.free(): "If finalization is desired, use delete
instead."
Should probably changed to "If finalization is desired, call clear() on
the Object/struct before calling GC.free()" or something like that.

I just noticed something regarding clear():
  struct Bar { ... }
  Bar *b = new Bar();
This compiles without any warning, but doesn't call the destructor:
  clear(b);
This compiles without any warning and actually does what you'd expect,
i.e. calls destructor:
  clear(*b);

Is this behavior expected/wanted? If not: Is it a known bug? I couldn't find it in the bugtracker, but maybe I searched for the wrong keywords.

Cheers,
- Daniel
April 27, 2011
On Tue, 26 Apr 2011 19:45:21 -0400, Daniel Gibson <metalcaedes@gmail.com> wrote:

> I just noticed something regarding clear():
>   struct Bar { ... }
>   Bar *b = new Bar();
> This compiles without any warning, but doesn't call the destructor:
>   clear(b);
> This compiles without any warning and actually does what you'd expect,
> i.e. calls destructor:
>   clear(*b);
>
> Is this behavior expected/wanted? If not: Is it a known bug? I couldn't
> find it in the bugtracker, but maybe I searched for the wrong keywords.

Let's start with class references.  Because class references cannot be separated from its reference, you have to finalize the class when finalizing the reference, because there's no way to say "clear what this reference refers to" vs. "clear this reference".  So you have to give a way to finalize a class instance.

With pointers, however, you can specify as you say, whether you want to clear the pointer or the struct itself.

Now, is it much useful to clear a pointer without clearing what it points to?  I'd say no, clearing a pointer is as easy as doing ptr = null.  So I'm thinking, it should be filed as a bug.

The obvious thing to decide is, what should be done on references to references?  If you clear a double pointer, should it go through both pointers?  Or a pointer to a class reference?

I'd say no, but you have to take extra steps to ensure it is this way.

-Steve