www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to make a generic function to take a class or struct by reference?

reply JN <666total wp.pl> writes:
I would like to have only one definition of getX if possible, 
because they both are doing the same thing. I can't remove the 
ref one, because without a ref it will pass the struct as a 
temporary and compiler won't like that.

```d
import std.stdio;

struct Foo
{
     int x;

     void doStuff()
     {
         *getX(this) = 5;
     }
}

class Bar
{
     int x;

     void doStuff()
     {
         *getX(this) = 5;
     }
}

int* getX(T)(ref T t)
{
     return &t.x;
}

int* getX(T)(T t)
{
     return &t.x;
}

void main()
{
     Foo foo;
     Bar bar = new Bar();

     foo.doStuff();
     bar.doStuff();

     assert(foo.x == 5);
     assert(bar.x == 5);
}
```
Mar 27 2022
next sibling parent drug <drug2004 bk.ru> writes:
Auto ref?

```D

int* getX(T)(auto ref T t)
{
     ...
```
Mar 27 2022
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/27/22 12:27 PM, JN wrote:
 int* getX(T)(T t)
 {
      return &t.x;
 }
Remove this one. It's a bad idea. It probably won't compile, but if it does, you will be using corrupted memory. -Steve
Mar 27 2022
prev sibling next sibling parent reply vit <vit vit.vit> writes:
On Sunday, 27 March 2022 at 16:27:44 UTC, JN wrote:
 I would like to have only one definition of getX if possible, 
 because they both are doing the same thing. I can't remove the 
 ref one, because without a ref it will pass the struct as a 
 temporary and compiler won't like that.

 ```d
 import std.stdio;

 struct Foo
 {
     int x;

     void doStuff()
     {
         *getX(this) = 5;
     }
 }

 class Bar
 {
     int x;

     void doStuff()
     {
         *getX(this) = 5;
     }
 }

 int* getX(T)(ref T t)
 {
     return &t.x;
 }

 int* getX(T)(T t)
 {
     return &t.x;
 }

 void main()
 {
     Foo foo;
     Bar bar = new Bar();

     foo.doStuff();
     bar.doStuff();

     assert(foo.x == 5);
     assert(bar.x == 5);
 }
 ```
Try this: ```d int* getX(T)(return auto ref T t){ static if(is(T == class)) return &t.x; else static if(is(T == struct) && __traits(isRef, t)) return &t.x; else static assert(0, "no impl " ~ T.stringof); } ``` or: ```d int* getX(T)(return auto ref T t) if(is(T == class) || (is(T == struct) && __traits(isRef, t))){ return &t.x; } ```
Mar 27 2022
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/27/22 22:32, vit wrote:

 int* getX(T)(return auto ref T t)
 if(is(T == class) || (is(T == struct) && __traits(isRef, t))){
      return &t.x;
 }
I also think 'return' parameter is needed but I could not come up with an example where that 'return' provides any more safety. Compiler already warns without it. (I tried with and without -preview=dip1000 -preview=dip25). Ali
Mar 28 2022
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/27/22 09:27, JN wrote:
 I would like to have only one definition of getX if possible, because
 they both are doing the same thing. I can't remove the ref one, because
 without a ref it will pass the struct as a temporary and compiler won't
 like that.
Combining all responses, the code at the bottom is a working example. First, we should remove the by-value getX function but then class function fails expectedly: class Bar { int x; void doStuff() { *getX(this) = 5; // <-- Compilation ERROR } } It may be worth going over why it fails to compile: 'this' happens to be of type Bar. Since classes are reference types, the 'this' is of type Bar (a reference) but it is an rvalue. (It is an rvalue because there is no one variable 'this' for this or any object.) rvalues cannot be passed by-reference; so the compilation fails. So, assuming the by-value getX() is removed, the following would make the code compile: class Bar { int x; void doStuff() { auto this_ = this; *getX(this_) = 5; // <-- Now compiles } } In that case we pass 'this_', which is clearly an lvalue and it can be passed by-reference. (lvalue becaues it is a variable sitting on the stack.) But we don't want to do the above everywhere, so 'auto ref' is a solution. The reason is, it would copy the rvalue (the object reference) arguments and it wouldn't be an error because the copied rvalue would be another reference to the same class object and it would work. import std.stdio; struct Foo { int x; void doStuff() { *getX(this) = 5; } } class Bar { int x; void doStuff() { *getX(this) = 5; } } auto getX(T)(auto ref T t) { return &t.x; } void main() { Foo foo; Bar bar = new Bar(); foo.doStuff(); bar.doStuff(); assert(foo.x == 5); assert(bar.x == 5); } Ali
Mar 28 2022