Consistency, Templates, Constructors, and D3

Era Scarecrow rtcvb32 at yahoo.com
Mon Aug 27 17:31:12 PDT 2012


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.

>> 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