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