Jump to page: 1 2 3
Thread overview
Using in as a parameter qualifier
May 31, 2013
Shriramana Sharma
May 31, 2013
Ali Çehreli
May 31, 2013
Shriramana Sharma
May 31, 2013
Ali Çehreli
May 31, 2013
Shriramana Sharma
May 31, 2013
Ali Çehreli
May 31, 2013
Jesse Phillips
May 31, 2013
Jonathan M Davis
May 31, 2013
Jonathan M Davis
May 31, 2013
Shriramana Sharma
Oct 22, 2014
Shriramana Sharma
Oct 22, 2014
Jonathan M Davis
Oct 23, 2014
Shriramana Sharma
Oct 23, 2014
Marc Schütz
Oct 23, 2014
Marc Schütz
May 31, 2013
Shriramana Sharma
May 31, 2013
bearophile
May 31, 2013
Jonathan M Davis
May 31, 2013
Shriramana Sharma
Jun 01, 2013
Ali Çehreli
Jun 01, 2013
Shriramana Sharma
Jun 01, 2013
Ali Çehreli
Jun 01, 2013
Shriramana Sharma
Jun 01, 2013
Jonathan M Davis
Jun 01, 2013
Jonathan M Davis
Jun 01, 2013
Jonathan M Davis
May 31, 2013
Hello people.

I have a pair type defined as :

struct pair {
  double x,y ;
  this () {}
  this (double x, double y) { this.x = x ; this.y = y ; }
}

Consider a function that operates on a pair:

double abs2 ( pair a ) { return a.x * a.x + a.y * a.y ; }

In C++ the function signature would be: double abs2 ( const pair & a )

So I thought const ref pair a would be appropriate in D -- is that right?

How about the "in" qualifier? Does it only replace C++'s const in this case or does it also guarantee that the object will not be copied (&/ref) ?

Also: does D not require member initializer lists like in C++? In C++
I would write the this(double,double) above as: pair(double x, double
y):x(x),y(y){} -- since that language is supposed to guarantee that
once we enter the { of the constructor, all sub-objects are
initialized.

Thanks!

-- 
Shriramana Sharma ஶ்ரீரமணஶர்மா श्रीरमणशर्मा
May 31, 2013
On 05/30/2013 10:36 PM, Shriramana Sharma wrote:

> struct pair {

It is more common to start type names with a capital letter: Pair.

>    double x,y ;
>    this () {}

That is not allowed. In D, every type has the .init property, which is its default value.

>    this (double x, double y) { this.x = x ; this.y = y ; }
> }

That is sensible to ensure that both values are provided. Otherwise, the following struct definition is sufficient:

struct Pair {
  double x, y;
}

void main()
{
    auto p0 = Pair();
    auto p1 = Pair(1.5);        // only p1.y is 0
    auto p2 = Pair(2.5, 3.5);
}

You can also set the .init value of the whole type:

struct Pair {
    double x = 10.75;
    double y = 20.25;
}

> Consider a function that operates on a pair:
>
> double abs2 ( pair a ) { return a.x * a.x + a.y * a.y ; }
>
> In C++ the function signature would be: double abs2 ( const pair & a )
>
> So I thought const ref pair a would be appropriate in D -- is that right?

Yes:

double abs2(ref const(Pair) a) { /* ... */ }

> How about the "in" qualifier? Does it only replace C++'s const in this
> case or does it also guarantee that the object will not be copied
> (&/ref) ?

'in' is nothing but 'scope const' (scope is not implemented yet):

double abs2(in Pair a) { /* ... */ }

Of course, pass-by-vale (the other C++ option) is also possible:

double abs2(Pair a) { /* ... */ }

This one has the added benefit of compiler's automatic moving of the value to the function. You know that 'a' is a copy of the argument so you can safely move it for use later.

> Also: does D not require member initializer lists like in C++? In C++
> I would write the this(double,double) above as: pair(double x, double
> y):x(x),y(y){} -- since that language is supposed to guarantee that
> once we enter the { of the constructor, all sub-objects are
> initialized.

D guarantees that every member is already initialized by their .init value when the constructor is entered (unless the initial value is 'void'). There are optimizations opportunities through flow analysis but the compiler does not apply them yet.

Ali

May 31, 2013
On Fri, May 31, 2013 at 12:12 PM, Ali Çehreli <acehreli@yahoo.com> wrote:
>>    double x,y ;
>>    this () {}
>
> That is not allowed. In D, every type has the .init property, which is its default value.

Hm can you clarify that a bit? If I actually try it I get:

pair.d(14): Error: constructor pair.pair.this default constructor for structs only allowed with @disable and no body

>> Consider a function that operates on a pair:
>> double abs2 ( pair a ) { return a.x * a.x + a.y * a.y ; }
>> In C++ the function signature would be: double abs2 ( const pair & a )
>> So I thought const ref pair a would be appropriate in D -- is that right?
>
> Yes:
> double abs2(ref const(Pair) a) { /* ... */ }

But is this the idiomatic D way of doing things? I mean, would one normally prefer in or ref const(T) which somehow seems more awkward than even const T &?

> 'in' is nothing but 'scope const' (scope is not implemented yet):

Does that mean "this is const within the current scope"?

And does "in" *not* guarantee that the object is *not* copied? I mean, if a parameter input to a function is read-only, it makes optimization sense to not copy it but just automatically provide a reference to it right? So I would expect in to mean const ref -- doesn't it work that way, and if not, why not?

> double abs2(Pair a) { /* ... */ }
> This one has the added benefit of compiler's automatic moving of the value
> to the function. You know that 'a' is a copy of the argument so you can
> safely move it for use later.

I don't understand; can you please clarify that. I understand that a is a copy of whatever pair is passed to it, so where do I move it and why would I want to use it later?

-- 
Shriramana Sharma ஶ்ரீரமணஶர்மா श्रीरमणशर्मा
May 31, 2013
On Fri, May 31, 2013 at 12:12 PM, Ali Çehreli <acehreli@yahoo.com> wrote:
> struct Pair {
>   double x, y;
> }
>
> void main()
> {
>     auto p0 = Pair();
>     auto p1 = Pair(1.5);        // only p1.y is 0

Wouldn't p1.y actually be NaN and not 0?

-- 
Shriramana Sharma ஶ்ரீரமணஶர்மா श्रीरमणशर्मा

May 31, 2013
Shriramana Sharma:

> Wouldn't p1.y actually be NaN and not 0?

Right. You can verify it with a writeln.

Bye,
bearophile
May 31, 2013
On Friday, May 31, 2013 15:10:01 Shriramana Sharma wrote:
> On Fri, May 31, 2013 at 12:12 PM, Ali Çehreli <acehreli@yahoo.com> wrote:
> >> Consider a function that operates on a pair:
> >> double abs2 ( pair a ) { return a.x * a.x + a.y * a.y ; }
> >> In C++ the function signature would be: double abs2 ( const pair & a )
> >> So I thought const ref pair a would be appropriate in D -- is that right?
> > 
> > Yes:
> > double abs2(ref const(Pair) a) { /* ... */ }
> 
> But is this the idiomatic D way of doing things? I mean, would one normally prefer in or ref const(T) which somehow seems more awkward than even const T &?

In D, const ref does not accept rvalues (unlike C++'s const &). So, if you use const ref, odds are that you need to duplicate the function

double abs2(ref const(Pair) a) {...}
double abs2(const Pair a) { abs2(a); /* calls the const ref overload */ }

If a function is templated, you can use auto ref to avoid the duplication

double abs2()(auto ref Pair a) {...}

or if you want const

double abs2()(auto ref const Pair a) {...}

auto ref is not currently implemented for non-templated functions, so if you want to use it, you have to templatize the function (the two examples above have empty template parameter lists just to templatize the function). auto ref makes it so that the function will accept both lvalues and rvalues (it generates a function that accepts by ref for lvalues and one which accepts by value for rvalues).

I would advise against ever using in. It's an alias for const scope, and scope is not currently fully implemented. scope is intended to disallow escaping from the function. So, with a non-reference type, using scope is pointless. And with a reference type, if/when scope is fully implemented later, you could end up with compilation errors because you _were_ escaping a scope parameter (e.g. by returning it or by assigning it to a static variable). At present, scope is at least partially implemented with delegates, but that's it. So, I'd advise against using scope (and thus in) with anything other than delegates. The primary benefit of using it with delegates is it allows the compiler to avoid allocating a closure when the function is called.

> > 'in' is nothing but 'scope const' (scope is not implemented yet):
> Does that mean "this is const within the current scope"?
> 
> And does "in" *not* guarantee that the object is *not* copied? I mean, if a parameter input to a function is read-only, it makes optimization sense to not copy it but just automatically provide a reference to it right? So I would expect in to mean const ref -- doesn't it work that way, and if not, why not?

in most definitely does _not_ guarantee that the object is not copied. Quite the opposite. It's an alias for const scope. If you want to avoid the copy, you need to use ref. in meant something slightly different in D1, and it was kept around for D2 but with the differences in the type system between them, it was decided to make it an alias for const scope. Personally, I wish that it was just gotten rid of outright due to the issues with scope and the confusion that it causes when people want to know what in does, but I guess that they wanted to reduce how much code broke when porting from D1 to D2.

- Jonathan M Davis
May 31, 2013
On 05/31/2013 02:40 AM, Shriramana Sharma wrote:

> On Fri, May 31, 2013 at 12:12 PM, Ali Çehreli <acehreli@yahoo.com> wrote:
>>>     double x,y ;
>>>     this () {}
>>
>> That is not allowed. In D, every type has the .init property, which is its
>> default value.
>
> Hm can you clarify that a bit? If I actually try it I get:
>
> pair.d(14): Error: constructor pair.pair.this default constructor for
> structs only allowed with @disable and no body

For structs, the default value of an object is required to be known at compile time, which is its default value. To enforce that rule, the default constructor cannot be provided for structs.

What the error message is saying is that you can declare it just to disable its use:

struct Pair {
    double x = 10.75;
    double y = 20.25;

    // Default constructor disabled
    @disable this();

    // Users must use another constructor:
    this(double x, double y) { this.x = x; this.y = y; }
}

As you see, you must also provide a proper constructor that is appropriate for that type.

>>> Consider a function that operates on a pair:
>>> double abs2 ( pair a ) { return a.x * a.x + a.y * a.y ; }
>>> In C++ the function signature would be: double abs2 ( const pair & a )
>>> So I thought const ref pair a would be appropriate in D -- is that right?
>>
>> Yes:
>> double abs2(ref const(Pair) a) { /* ... */ }
>
> But is this the idiomatic D way of doing things? I mean, would one
> normally prefer in or ref const(T) which somehow seems more awkward
> than even const T &?

Although 'in' feels like it would make all the sense for an in-parameter (at least because it conveys our intent to the compiler), Jonathan explained why 'in' is disappointing.

I still use 'in' in the examples in many chapters, which needs to be corrected at some point:

  http://ddili.org/ders/d.en/function_parameters.html

I gave a talk at DConf 2013, which included D's move semantics (starting on slide 11):

  http://dconf.org/talks/cehreli.html

Interestingly, that talk is already old :) due to the improvements in dmd 2.063. Here is an adaptation of an example on the dmd 2.063 changelog:

import std.stdio;

struct S
{
    this(int i)           { writeln("1"); }
    this(int i) const     { writeln("2"); }
    this(int i) immutable { writeln("3"); }
    this(int i) shared    { writeln("4"); }
}

void main()
{
    auto a = new S;           // writes "1"
    auto b = new const S;     // writes "2"
    auto c = new immutable S; // writes "3"
    auto d = new shared S;    // writes "4"
}

D's move semantics are explained in the following blog posts by Bartosz Milewski:


http://bartoszmilewski.com/2008/10/18/who-ordered-rvalue-references-part-1/


http://bartoszmilewski.com/2008/10/26/who-ordered-rvalue-references-part-2/


http://bartoszmilewski.com/2008/11/03/who-ordered-rvalue-references-part-3/

So, I would use 'ref const' only in special cases and only after profiling proves that there would be no performance penalty for doing that. After all, ref is implemented by a pointer and the indirect access to the members of the object through that pointer may be slow depending on the application. That indirection may even cause the CPU access outside of its caches, which is a relatively very slow operation.

However, my short tests on this has demonstrated that by-ref is faster with today's dmd even on a contrived program that accesses to random elements of huge arrays.

However, :) if you are going to make a copy of the argument anyway, always take a struct by-value. That works with both lvalue and rvalue arguments and in the case of rvalues, you will get the automatic move semantics.

My choice is by-value for structs unless there is any reason not to. Also note that by-ref-to-const is an anti-idiom even in C++ even in surprising places. Everybody defines operator= by taking reference to const, which may be slower than taking by-value. This is the idiomatic way of writing operator= in C++:

    Foo & operator= (Foo that)
    {
        this->swap(that);
        return *this;
    }

> it makes optimization sense to not copy it but just automatically provide a
> reference to it right?

Unless the language makes a guarantee about that we cannot take the address of the parameter and save it. We wouldn't know whether it is a copy or a reference.

> So I would expect in to mean const ref -- doesn't it work that way, and if
> not, why not?

Another reason is less surprise because structs have copy semantics by default.

Ali

May 31, 2013
Thanks to all who explained the various issues. Some more questions:

On Fri, May 31, 2013 at 9:28 PM, Ali Çehreli <acehreli@yahoo.com> wrote:
> What the error message is saying is that you can declare it just to disable its use:
>
> struct Pair {
>     double x = 10.75;
>     double y = 20.25;
>
>     // Default constructor disabled
>     @disable this();
>
>     // Users must use another constructor:
>     this(double x, double y) { this.x = x; this.y = y; }
> }
>
> As you see, you must also provide a proper constructor that is appropriate for that type.

Sorry but I still don't get it -- if a default constructor is disallowed for struct-s by the language itself, why should I have to *tell* the compiler to disable it?

> However, :) if you are going to make a copy of the argument anyway, always take a struct by-value. That works with both lvalue and rvalue arguments and in the case of rvalues, you will get the automatic move semantics.
>
> My choice is by-value for structs unless there is any reason not to.

So where is the cut-off point? I mean, by-value means a copy is done every time the function is called right? So how heavy (in terms of sizeof) would a struct have to be to make passing by ref more efficient than passing by value?

> be slower than taking by-value. This is the idiomatic way of writing operator= in C++:
>
>     Foo & operator= (Foo that)
>     {
>         this->swap(that);
>         return *this;
>     }

This assumes that Foo defines a swap method. Maybe good for lists and such. ... One sec... if you take an argument by value, it means the copy constructor would be called. So how is this really more efficient than taking const Foo & as an argument? Is it the code savings that would be done by avoiding duplicating the copy-constructor code in operator= too?

>> it makes optimization sense to not copy it but just automatically provide
>> a
>> reference to it right?
>
> Unless the language makes a guarantee about that we cannot take the address of the parameter and save it. We wouldn't know whether it is a copy or a reference.

Can you explain? It's not very clear to me.

> Another reason is less surprise because structs have copy semantics by default.

Makes me start thinking I should use class rather than struct for my pair even though there is no polymorphism etc required. Then I can just say abs2(const pair). It would automatically be a reference. Would that be inadvisable for any reason? My program would use pairs heavily. Would the heap allocation/deallocation/GC be a burden if I made the pair a class?

-- 
Shriramana Sharma ஶ்ரீரமணஶர்மா श्रीरमणशर्मा
May 31, 2013
On 05/31/2013 10:49 AM, Shriramana Sharma wrote:

>> struct Pair {
>>      double x = 10.75;
>>      double y = 20.25;
>>
>>      // Default constructor disabled
>>      @disable this();
>>
>>      // Users must use another constructor:
>>      this(double x, double y) { this.x = x; this.y = y; }
>> }
>>
>> As you see, you must also provide a proper constructor that is appropriate
>> for that type.
>
> Sorry but I still don't get it -- if a default constructor is
> disallowed for struct-s by the language itself, why should I have to
> *tell* the compiler to disable it?

Not when you "have to", when you "want to" disable it.

>> However, :) if you are going to make a copy of the argument anyway, always
>> take a struct by-value. That works with both lvalue and rvalue arguments and
>> in the case of rvalues, you will get the automatic move semantics.
>>
>> My choice is by-value for structs unless there is any reason not to.
>
> So where is the cut-off point? I mean, by-value means a copy is done
> every time the function is called right? So how heavy (in terms of
> sizeof) would a struct have to be to make passing by ref more
> efficient than passing by value?

Nobody knows. The cut-off point would move at any direction by the next generation of CPUs and systems.

>> be slower than taking by-value. This is the idiomatic way of writing
>> operator= in C++:
>>
>>      Foo & operator= (Foo that)
>>      {
>>          this->swap(that);
>>          return *this;
>>      }
>
> This assumes that Foo defines a swap method.

This is not a C++ forum, but... :) Yes, it must define a non-throwing swap member function. That function is almost always extremely fast.

> Maybe good for lists and
> such. ... One sec... if you take an argument by value, it means the
> copy constructor would be called.

We are talking about assignment, which happens to be copy+destroy. No problem there.

> So how is this really more efficient
> than taking const Foo & as an argument?

It as efficient as an exception-safe operator=. It is not as efficient as incorrectly written operator=.

>>> it makes optimization sense to not copy it but just automatically provide
>>> a
>>> reference to it right?
>>
>> Unless the language makes a guarantee about that we cannot take the address
>> of the parameter and save it. We wouldn't know whether it is a copy or a
>> reference.
>
> Can you explain? It's not very clear to me.

I thought your proposal was "compiler should automatically provide a reference when it makes sense to."

I meant: Assume that you wrote such a function where the compiler can play such a trick. You would not be sure whether you can take the address of the parameter and store it for later use, even inside the function, even to print a diagnostic message. The reason is, you wouldn't know whether the parameter was a reference to the argument or a copy of it.

>> Another reason is less surprise because structs have copy semantics by
>> default.
>
> Makes me start thinking I should use class rather than struct for my
> pair even though there is no polymorphism etc required. Then I can
> just say abs2(const pair). It would automatically be a reference.
> Would that be inadvisable for any reason? My program would use pairs
> heavily. Would the heap allocation/deallocation/GC be a burden if I
> made the pair a class?

Sounds like you really have to run some test code to figure out what will work best for you. But in theory, no, your pair is not a class. It is a simple value type.

If you will have member functions, classes will be slower unless you mark those member function as 'final'. Also, class variables always bring an indirection because they are reference types.

Ali

May 31, 2013
On Friday, May 31, 2013 23:19:57 Shriramana Sharma wrote:
> > However, :) if you are going to make a copy of the argument anyway, always take a struct by-value. That works with both lvalue and rvalue arguments and in the case of rvalues, you will get the automatic move semantics.
> > 
> > My choice is by-value for structs unless there is any reason not to.
> 
> So where is the cut-off point? I mean, by-value means a copy is done every time the function is called right?

The compiler will move an object rather than copy it when it can. So, for instance, if you pass the function a temporary, it'll move that temporary rather than copying it. However, if it's called with an lvalue, odds are that it's going to have to make a copy (though it might be moved if it were the last time in the caller that the variable was referenced).

> So how heavy (in terms of
> sizeof) would a struct have to be to make passing by ref more
> efficient than passing by value?

That would depend on the compiler. You'd have to do tests to see. Certainly, for anything that's small enough to fit in a register, it's likely to be faster to pass by value. But how much beyond that the struct has to grow before it's large enough that passing by reference would be cheaper, I don't know. A lot that the compiler does could affect that. The only way to know for sure is to test it.

- Jonathan M Davis
« First   ‹ Prev
1 2 3