October 10, 2014
On Friday, 10 October 2014 at 01:32:54 UTC, dcrepid wrote:
> On Sunday, 27 November 2011 at 19:50:24 UTC, deadalnix wrote:
>> Hi,
>>
>> I wonder why struct can't have a default constructor...
>
> I know this is an old thread, but I've run into this same problem recently and search yielded this result.
>
> I myself have tried working around the default-constructor problem with things like
>
> this(bool bInit = true)
>
> - which of course doesn't get invoked with MyStruct(), even with @disable this.

You can use `static opCall` as a workaround. The following prints "S(0)" and "S(3)":

    struct S {
        int x;
        static S opCall() {
            S s;
            s.x = 3;
            return s;
        }
    }

    void main() {
        import std.stdio;
        S s;
        writeln(s);
        S t = S();
        writeln(t);
    }

But if you add a constructor with parameters, you get an error:

    struct S {
        int x;
        static S opCall() { ... }
        this(int y) { x = y; }
    }

xx.d(4): Error: struct xx.S static opCall is hidden by constructors and can never be called
xx.d(4):        Please use a factory method instead, or replace all constructors with static opCall.

IMO this is too restrictive, as obviously, the static opCall is _not_ hidden by the constructor.
October 10, 2014
On 11/27/2011 11:53 AM, deadalnix wrote:
> I wonder why struct can't have a default constructor. TDPL state that it is
> required to allow every types to have a constant .init .

Having a .init instead of a default constructor has all kinds of useful properties:

1. the default object is trivially constructable and cannot fail

2. an easy way for other constructors to not have overlooked field initializers, so they get garbage initialized like in C++

3. generic code can rely on the existence of trivial construction that cannot fail

4. dummy objects can be created, useful for "does this compile" semantics

5. an object can be "destroyed" by overwriting it with .init (overwriting with 0 is not the same thing)

6. when you see:
    T t;
in code, you know it is trivially constructed and will succeed

7. objects can be created using TypeInfo


Default constructors are baked into C++. I can't escape the impression that the desire for D default constructors comes from more or less trying to write C++ style code in D.

I feel that non-trivial default construction is a bad idea, as are the various methods people try to get around the restriction. For non-trivial construction, one can easily just make a constructor with an argument, or call a factory method that returns a constructed object.

October 10, 2014
On Friday, 10 October 2014 at 09:58:54 UTC, Walter Bright wrote:
> On 11/27/2011 11:53 AM, deadalnix wrote:
>> I wonder why struct can't have a default constructor. TDPL state that it is
>> required to allow every types to have a constant .init .
>
> Having a .init instead of a default constructor has all kinds of useful properties:
>
> 1. the default object is trivially constructable and cannot fail
>
> 2. an easy way for other constructors to not have overlooked field initializers, so they get garbage initialized like in C++
>
> 3. generic code can rely on the existence of trivial construction that cannot fail
>
> 4. dummy objects can be created, useful for "does this compile" semantics
>
> 5. an object can be "destroyed" by overwriting it with .init (overwriting with 0 is not the same thing)
>
> 6. when you see:
>     T t;
> in code, you know it is trivially constructed and will succeed
>
> 7. objects can be created using TypeInfo
>
>
> Default constructors are baked into C++. I can't escape the impression that the desire for D default constructors comes from more or less trying to write C++ style code in D.

Bit OT: What is The D code style then? It would be very useful for those coming from C++ to have a wiki/article on how to translate C++ idioms and practices to D. I too am writing D code like I do my C++ and often find myself puzzled (deterministic d-tors being perfect example). Missing default struct c-tor is also one of such examples - and adding opCall() feels hacky.


October 10, 2014
On Fri, 10 Oct 2014 02:58:39 -0700
Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:

> Default constructors are baked into C++. I can't escape the impression that the desire for D default constructors comes from more or less trying to write C++ style code in D.
second this. people also keep forgetting about default values and want default constructors to simulate this:

  struct {
    int n = 42;
    string s = "default string";
  }


October 10, 2014
On 10/10/2014 3:23 AM, Szymon Gatner wrote:
> Bit OT: What is The D code style then? It would be very useful for those coming
> from C++ to have a wiki/article on how to translate C++ idioms and practices to
> D. I too am writing D code like I do my C++ and often find myself puzzled
> (deterministic d-tors being perfect example). Missing default struct c-tor is
> also one of such examples - and adding opCall() feels hacky.

You're right, but it's a bit of a difficult question to answer. Other examples that cause people grief:

1. using ref as a type constructor
2. multiple inheritance
3. using a struct as both a value and a polymorphic type

These are idiomatic C++ usages that need to be rethought for D.

October 10, 2014
On 10/10/2014 3:25 AM, ketmar via Digitalmars-d wrote:
> people also keep forgetting about default values and want
> default constructors to simulate this:
>
>    struct {
>      int n = 42;
>      string s = "default string";
>    }

That's a good observation.
October 10, 2014
On Friday, 10 October 2014 at 08:03:49 UTC, ketmar via Digitalmars-d wrote:
> On Fri, 10 Oct 2014 01:32:51 +0000
> dcrepid via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>> So no possibility of using objects as resource acquire/release mechanisms.
>> I assume using scoped with class objects will have a similar problem..
> no, stack-allocated object is GC root, so anything it holds reference
> to will not be destroyed until stack object is alive. so destructor of
> stack-allocated object *can* be used for doing cleanup.
>
> but you can use templates to build your "anchor" structs. it's hard to
> say without concrete code samples.

What I mean with the scoped objects is that you still need to provide a constructor with parameters, yes? I will have to try this out myself, but I've avoided it since some people seem to indicate this is going to be deprecated..

Basically what I was doing code-wise was creating a 'FixedBuffer' structure, which would have its size passed as a template parameter.  The constructor would do a malloc on it, and all the checks for bounds would only need to rely on that template parameter. Net result is that the cost of having a pretty safe bounds-checked buffer was just the size of one pointer.

I *sorta* get the whole concept of being able to easily reason and work with structures in D, but why then does it have a destructor and a way to disable a default constructor, along with operator overloads and whatnot.  It seems to me its trying to be object-like and POD-like at the same time which doesn't mesh well.

As far as the 'scoped' object, I was referring to default construction - is it possible?  Or do I need to use Walter's factory production workaround everytime I want something with deterministic create/destroy properties in this context?

I would very readily accept using objects over structs if they had a guarantee of when they are destroyed, and weren't as risky to pass around as C pointers (i.e. possibility of them being null).
October 10, 2014
On Fri, 10 Oct 2014 18:50:29 +0000
dcrepid via Digitalmars-d <digitalmars-d@puremagic.com> wrote:

> What I mean with the scoped objects is that you still need to provide a constructor with parameters, yes? I will have to try this out myself, but I've avoided it since some people seem to indicate this is going to be deprecated..
only the syntax `scope auto a = new A();` is deprecated, not the whole concept. you just have to write it this now:

  import std.typecons : scoped; // yep, it's in a library now
  ...
  class A {
    this () { ... }
    ~this () { ... }
  }
  ...
  {
    // allocate class instance on the stack
    auto a = scoped!A();
    ...
  } // here destructor for 'a' will be called

just be careful and don't let 'a' escape the scope. ;-)

> Basically what I was doing code-wise was creating a 'FixedBuffer' structure, which would have its size passed as a template parameter.  The constructor would do a malloc on it, and all the checks for bounds would only need to rely on that template parameter. Net result is that the cost of having a pretty safe bounds-checked buffer was just the size of one pointer.
it's better to use slices for that. you can create struct like this:

  struct A(size_t asize) {
    private ubyte* mData/* = null*/; // '=null' is the default
    private enum mSize = asize;

    @disable this (this);

    ~this () {
      import core.stdc.stdlib;
      if (mData !is null) free(mData);
    }

    // and allocate memory for data in getter
    @property auto data () {
      import core.stdc.stdlib;
      if (mData is null) {
        import core.exception : onOutOfMemoryError;
        mData = cast(typeof(mData))malloc(mSize);
        if (mData is null) onOutOfMemoryError();
      }
      return mData[0..mSize];
    }

    @property size_t length () const @safe pure nothrow @nogc { return mSize; }
    size_t opDollar () const @safe pure nothrow @nogc { return mSize; }

    ubyte opIndex (size_t ofs) @trusted {
      import core.exception : RangeError;
      if (ofs >= mSize) throw new RangeError();
      return data[ofs];
    }

    ubyte opIndexAssign (ubyte v, size_t ofs) @trusted {
      import core.exception : RangeError;
      if (ofs >= mSize) throw new RangeError();
      return (data[ofs] = v);
    }
  }

but then you will not be able to copy it (or you'll need additional field to keep "don't free" flag, and with it you can use slice instead, 'cause they both will not fit into the pointer). and you will not be able to pass it to functions anyway, this will not work:

  void doSomethingOnBuffer (ref A buf);

only this:

  void doSomethingOnBuffer (ref A!1024 buf);

i.e. you'll have to write the size in function args.

> I *sorta* get the whole concept of being able to easily reason and work with structures in D, but why then does it have a destructor and a way to disable a default constructor, along with operator overloads and whatnot.  It seems to me its trying to be object-like and POD-like at the same time which doesn't mesh well.
'cause there are two kinds of structs, actually. one kind is POD, and another is object-like, with methods, destructors and so on. this may be confusing a little. ;-) just don't mix 'em.

> As far as the 'scoped' object, I was referring to default construction - is it possible?
you can use classes, as i written above. just don't store such instance anywhere, 'cause it will be overwritten with another data after exiting the scope and all hell breaks loose.

> I would very readily accept using objects over structs if they had a guarantee of when they are destroyed, and weren't as risky to pass around as C pointers (i.e. possibility of them being null).
you can pass pointers to structs. pointers can be null. ;-)


October 10, 2014
On Sunday, 27 November 2011 at 19:50:24 UTC, deadalnix wrote:
> Hi,
>
> I wonder why struct can't have a default constructor. TDPL state that it is required to allow every types to have a constant .init .
>
> That is true, however not suffiscient. A struct can has a void[constant] as a member and this doesn't have a .init . So this limitation does not ensure that the benefit claimed is garanteed.
>
> Additionnaly, if it is the only benefit, this is pretty thin compared to the drawback of not having a default constructor.

Think the argument is that declaring `T t;` must be CTFE, which kind of implies a T.init state (which may have non-deterministic values in the presence of " = void").

This is mostly for declaring things static, and the whole "constructors are run after the .init blit".

But even then:
T t; //value is T.init, if not @disable this()
T t = T(); //Potentially run-time

I've started threads and tried to start discussions about this before, but to no avail. It's a relativelly recurrent complain, especially from "newer" C++ users. The older D users have either of thrown in the towel, or implemented "workarounds".
October 10, 2014
On Friday, 10 October 2014 at 20:25:16 UTC, ketmar via Digitalmars-d wrote:
> On Fri, 10 Oct 2014 18:50:29 +0000
> dcrepid via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>> What I mean with the scoped objects is that you still need to provide a constructor with parameters, yes? I will have to try this out myself, but I've avoided it since some people seem to indicate this is going to be deprecated..
> only the syntax `scope auto a = new A();` is deprecated, not the whole
> concept. you just have to write it this now:
>
>   import std.typecons : scoped; // yep, it's in a library now
>   ...
>   class A {
>     this () { ... }
>     ~this () { ... }
>   }
>   ...
>   {
>     // allocate class instance on the stack
>     auto a = scoped!A();
>     ...
>   } // here destructor for 'a' will be called
>
> just be careful and don't let 'a' escape the scope. ;-)

Great, thanks for the tips!

> it's better to use slices for that. you can create struct like this:
> (snipped)

Thanks for the effort of providing a proof of concept, even though I don't agree with what the data property should do. Shouldn't properties not mutate the data? But more than that I think its a waste to check whether the buffer is there every time you need to access it. Its better to allocate at the start, throw an exception if it can't be allocated, then provide access to it without any wasteful checks between. At least, this is the RAII type of idiom I'm after.

How I am currently using it is using either .ptr, .data, or slices to create the necessary access or D slices. It works pretty well for what I'm using it for. I'm interfacing with the Windows API with the .ptr property, and then when I need to use it with D functions I typically use opSlice since the data is often variable lengths.

I've pasted most of the struct I've been using here:
http://dpaste.dzfl.pl/15381d5828f8

I would use it in say, a call to Windows' WinHttpReadData() using OutBuffer.ptr, then work with the received data (stored in dwDownloaded) using OutBuffer[0 .. dwDownloaded], for example. It works pretty nicely even if its not up to D's standards.

About escaping scope - I'm aware this is possible, but the idea is that this is supposed to be used temporarily in a certain scope, then discarded (as the Scope prefix indicates).. there's better methods to doing it safely, for sure. But to do the same with only a single pointer?

I think I like the idea of the factory method now though, as I've learned you can hide a struct inside a function, and then call the function to set the struct up properly and return it. At least, I'm sure I've seen code that does that..


>>  It seems to me its trying to be object-like and POD-like at the same time which doesn't mesh well.
> 'cause there are two kinds of structs, actually. one kind is POD, and
> another is object-like, with methods, destructors and so on. this may be
> confusing a little. ;-) just don't mix 'em.

Haha.. I'll try not to (I think?)

>> I would very readily accept using objects over structs if they had a guarantee of when they are destroyed, and weren't as risky to pass around as C pointers (i.e. possibility of them being null).
> you can pass pointers to structs. pointers can be null. ;-)

I thought ref gives us the same guarantee that C++'s references do?  Pointers are so.. 90's =P  But yeah, the nullable object thing has bitten my ass a few times as I'm learning D, which really frustrates me =\

Thanks for your time