Consistency, Templates, Constructors, and D3
Jose Armando Garcia
jsancio at gmail.com
Mon Aug 27 18:32:31 PDT 2012
On Aug 27, 2012, at 17:31, "Era Scarecrow" <rtcvb32 at yahoo.com> wrote:
> On Monday, 27 August 2012 at 22:44:53 UTC, F i L wrote:
>> Era Scarecrow wrote:
>
>> C# structs are allocated on the stack when they can be. In certain
>> cases (they're class fields, they're boxed, etc..) they're heap
>> allocated.
>
> So it will intentionally ignore 'new' and instead just call the
> constructor and decide if it should be heap or not? Sounds both
> helpful and harmful to me.
It works very similar to D's struct. If declared in a function they
are stack allocated. If declared in a class they are heap allocated.
Not sure what they do for global/static declarations. Probably
allocated in the data section.
>
>>> By looking at newFoo I'd say a class; But if like in C# I'm sure
>>> you can't tell the difference (But C++ with the pointer you can).
>
>> the 'auto' keyword kind negates the 'Type*' distinction. My point
>> here is that you pretty much have to look up the type definition
>> (or a tooltip) to understand what you're working with when factory
>> functions are involved.
>
> I think that's why the naming should be something that sounds easy
> to follow or follows a particular style. The following should have
> no trouble figuring out even without seeing any documentation. Mind
> you i'm throwing this out there without a specific class in min.
>
> File inputFile = File.createHandle("some file");
> inputFile.load();
> inputFile.close();
>
>> The result is the same weather they're inside or outside a class,
>> because, when used, all the coder sees is the function name to know
>> about what it's returning.
>
> Wrong. If you have it within a class, you know it comes FROM that
> class. There may be multiple 'Records' depending on different
> projects or having compatible types. In my own factory function it
> polymorphs based on the input, so knowing the root class makes sense
> to me.
>
>> In D there is a difference between structs and classes beyond
>> what's in C++. The 'new' keyword helps us understand what kind of
>> object is being created, and I enjoy that. However, my argument is
>> in favor of consistency because right now factory-functions, which
>> _are_ used a lot, completely hide that distinction on top of being
>> inconsistent with "normal" type construction.
>
>>> And a postblits would end up being...? The extra 'this' makes it
>>> look like an obvious typo or a minor headache.
>>>
>>> this this(this){} //postblitz?
>>
>> I'm sure this case has an easy solution. How about:
>>
>> struct Foo {
>> this new() { ... } // constructor
>> this() { ... } // postblit
>> }
>
> But now you're breaking consistency by not including a return type.
> maybe 'this this()' but that looks like a mistake or typo. If you're
> willing to use something without a return type, why not leave it
> 'this(this)'? Or rename it all together? 'this postblitz()'.
>
>>> Only if you start getting creative can it begin to get confusing;
>>
>> And I have to completely disagree with you here. Memory Pools are
>> used everywhere in performance-critical code which needs a dynamic
>> array of objects. At least half of all the "allocation" in game
>> engines is done through factory functions that recycle objects.
>
> Wasn't there already a way to specify (or going to be) what you
> wanted to use an allocator? I thought i remember seeing an example
> in TDPL.
>
>> And for overload distinction (new vs load), which is an issue
>> beyond Memory Pools and effects and even larger codebase. There
>> needs to be a consistent way to distinguish (by name) a constructor
>> that loads from a file, and one that creates the object "manually".
>
> Isn't that more an API issue?
>
>>> If we take your approach and suggestion, which one should the
>>> compile assume?
>>>
>>> Something globalSomething;
>>>
>>> class Something {
>>> this defaultConstructor();
>>> this duplicate(); //or clone
>>> this copyGlobalSomething();
>>> this constructorWithDefault(int x = 100);
>>> }
>>>
>>> By signature alone... Which one? They are all legal, they are
>>> uniquely named, and they are all equal candidates. Order of
>>> functions are irrelevant.
>>
>> It could work identically to how D functions today. A 'new()'
>> constructor would be part of the root Object classes are derived
>> of, and structs would have an implicit 'new()' constructor.
>
> But new wouldn't be a constructor then would it? It would still be
> based on allocating memory that's optionally different. Constructor
> and allocation are two different steps; And for it to seamlessly go
> from one to another defaults to having a set default constructor.
> Let's assume...
>
> class Object {
> this new() {
> //allocate
> return defaultConstructor();
> }
> this defaultConstructor() {}
> }
>
> Now in order to make a constructor (and then destructor) you either
> can:
>
> A) overload or use 'defaultConstructor', which would be publicly known
> B) overload new to do allocation the same way and call a different
> constructor and specifically add a destructor to make sure it
> follows the same lines.
> C) overload new to call the default allocator and then call a
> different constructor
>
> Now assuming you can make a different constructor by name, you then
> have to be able to specify a destuctor the same way for consistancy.
>
> class CustomType {
> this MyAwesomeConstuctor();
> void MyAwesomeDestructor();
> }
>
> Same problem, how do you tell it ahead of time without completely
> rewriting the rules? leaving it as 'this' and '~this' are simple to
> remember and work with, and factory functions should be used to do a
> bulk of work when you don't want the basic/bare minimum.
>
>> This could work in our favor, because instead of 'new' we could use
>> 'alloc' (or something like that) while still encouraging 'new'.
>> Which means structs could provide a parameter-less new() constructor:
>> class Foo {
>> float x;
>> // no constructor
>> }
>>
>> auto f = Foo.alloc();
>> assert(f.x == float.nan);
>>
>> ---
>>
>> struct Point {
>> float x, y;
>> this new() { x = 0; y = 0; }
>> this new(float x, float y) { ... }
>> }
>>
>> auto a = Point.new();
>> auto b = Point.new(1, 2);
>> assert(a.x == 0.0f);
>
> After reading a large chunk where Andrei spoke of the flaws of C++'s
> classes and inheritance and potential problems having stack/exact
> size allocated classes compared to leaving them on the heap is
> coming to mind. This would undo all of that.
>
> class Bar : Foo {
> float y, z;
> }
>
> auto foo = Foo.alloc();
> int isSafe;
> foo = Bar.alloc(); //should implicity convert normally. But stack?
>
> assert(isSafe == int.init); //or was the next variables overwritten??
>
>
> In the cases where you don't overload anything would make the
> classes safe, in which case they don't polymorph, in which case you
> may as well use structs. Am I wrong on this?
More information about the Digitalmars-d
mailing list