www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Declaring rvalue function arguments

reply Matt Elkins <notreal fake.com> writes:
I know I can mark an argument ref to require lvalues, so I'm 
wondering whether there is an equivalent for rvalues; that is, is 
there a way to specify that an argument to a function MUST be an 
rvalue?

For example, in C++ I can do this:
[code]
void foo(int && x) {...}

foo(5); // Works fine
int y = 5;
foo(y); // Compile error; y is not an rvalue
[/code]

This functionality turns out to be really useful when dealing 
with transferring ownership of resources.
Jan 31 2016
next sibling parent maik klein <maikklein googlemail.com> writes:
On Sunday, 31 January 2016 at 17:21:54 UTC, Matt Elkins wrote:
 I know I can mark an argument ref to require lvalues, so I'm 
 wondering whether there is an equivalent for rvalues; that is, 
 is there a way to specify that an argument to a function MUST 
 be an rvalue?

 For example, in C++ I can do this:
 [code]
 void foo(int && x) {...}

 foo(5); // Works fine
 int y = 5;
 foo(y); // Compile error; y is not an rvalue
 [/code]

 This functionality turns out to be really useful when dealing 
 with transferring ownership of resources.
I am also very interested in this. I just asked this question today on SO https://stackoverflow.com/questions/35115702/how-do-i-express-ownership-semantics-in-d
Jan 31 2016
prev sibling parent reply anonymous <anonymous example.com> writes:
On 31.01.2016 18:21, Matt Elkins wrote:
 I know I can mark an argument ref to require lvalues, so I'm wondering
 whether there is an equivalent for rvalues; that is, is there a way to
 specify that an argument to a function MUST be an rvalue?

 For example, in C++ I can do this:
 [code]
 void foo(int && x) {...}

 foo(5); // Works fine
 int y = 5;
 foo(y); // Compile error; y is not an rvalue
 [/code]

 This functionality turns out to be really useful when dealing with
 transferring ownership of resources.
I don't know if this works in all cases, but it passes that simple test: ---- disable void foo(ref int x); void foo(int x) {} void main() { foo(5); /* works */ int y = 5; foo(y); /* error */ } ----
Jan 31 2016
next sibling parent reply maik klein <maikklein googlemail.com> writes:
On Sunday, 31 January 2016 at 17:42:19 UTC, anonymous wrote:
 On 31.01.2016 18:21, Matt Elkins wrote:
 I know I can mark an argument ref to require lvalues, so I'm 
 wondering
 whether there is an equivalent for rvalues; that is, is there 
 a way to
 specify that an argument to a function MUST be an rvalue?

 For example, in C++ I can do this:
 [code]
 void foo(int && x) {...}

 foo(5); // Works fine
 int y = 5;
 foo(y); // Compile error; y is not an rvalue
 [/code]

 This functionality turns out to be really useful when dealing 
 with
 transferring ownership of resources.
I don't know if this works in all cases, but it passes that simple test: ---- disable void foo(ref int x); void foo(int x) {} void main() { foo(5); /* works */ int y = 5; foo(y); /* error */ } ----
The problem is that x will be copied afaik which is not what you want if you want to deal with ownership.
Jan 31 2016
next sibling parent reply Matt Elkins <notreal fake.com> writes:
On Sunday, 31 January 2016 at 17:48:53 UTC, maik klein wrote:
 The problem is that x will be copied afaik which is not what 
 you want if you want to deal with ownership.
I think that can be solved by wrapping the resource in a struct that deals with passing the ownership. Here is the one I am using right now: [code] struct ResourceHandle(T, alias Deleter, T Default = T.init) { // Constructors/Destructor this(in T handle) {m_handle = handle;} disable this(this); ~this() {Deleter(m_handle);} // Operators disable void opAssign(ref ResourceHandle lvalue); ref ResourceHandle opAssign(ResourceHandle rvalue) {swap(m_handle, rvalue.m_handle); return this;} // Methods property T handle() const {return m_handle;} property T handle(T handle) {Deleter(m_handle); m_handle = handle; return m_handle;} T release() {T result = m_handle; m_handle = Default; return result;} private: T m_handle = Default; } [/code]
Jan 31 2016
parent Matt Elkins <notreal fake.com> writes:
On Sunday, 31 January 2016 at 18:02:19 UTC, Matt Elkins wrote:
 Here is the one I am using right now:
Actually, here is the whole module in case you are interested in the unittests/usage: [code] import std.algorithm; import std.traits; struct ResourceHandle(T, alias Deleter, T Default = T.init) { // Constructors/Destructor this(in T handle) {m_handle = handle;} disable this(this); ~this() {Deleter(m_handle);} // Operators disable void opAssign(ref ResourceHandle lvalue); ref ResourceHandle opAssign(ResourceHandle rvalue) {swap(m_handle, rvalue.m_handle); return this;} // Methods property T handle() const {return m_handle;} property T handle(T handle) {Deleter(m_handle); m_handle = handle; return m_handle;} T release() {T result = m_handle; m_handle = Default; return result;} private: T m_handle = Default; } nogc safe nothrow unittest { static uint destroyedCount; static uint lastDestroyed; alias RH = ResourceHandle!(uint, (uint resource){if (resource != uint.init) {lastDestroyed = resource; ++destroyedCount;}}); // Test basic resource cleanup assert(destroyedCount == 0); assert(lastDestroyed != 7); {auto handle0 = RH(7);} assert(destroyedCount == 1); assert(lastDestroyed == 7); // Test releasing { auto handle0 = RH(8); assert(handle0.handle == 8); assert(handle0.release() == 8); assert(handle0.handle == uint.init); assert(destroyedCount == 1); assert(lastDestroyed == 7); } assert(destroyedCount == 1); assert(lastDestroyed == 7); { // Test that copying and lvalue assignment are disabled auto handle0 = RH(5); static assert (!__traits(compiles, {auto handle1 = handle0;})); static assert (!__traits(compiles, {RH handle1; handle1 = handle0;})); // Test that rvalue assignment works auto makeRH(uint value) {return RH(value);} handle0 = makeRH(3); assert(destroyedCount == 2); assert(lastDestroyed == 5); } assert(destroyedCount == 3); assert(lastDestroyed == 3); // Test setting in static array { RH[3] handles; handles[0] = RH(9); assert(destroyedCount == 3); assert(lastDestroyed == 3); } assert(destroyedCount == 4); assert(lastDestroyed == 9); // Test setting to resource directly { auto handle0 = RH(11); assert(destroyedCount == 4); assert(lastDestroyed == 9); assert(handle0.handle == 11); handle0.handle = 12; assert(destroyedCount == 5); assert(lastDestroyed == 11); assert(handle0.handle == 12); } assert(destroyedCount == 6); assert(lastDestroyed == 12); } [/code]
Jan 31 2016
prev sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, January 31, 2016 17:48:53 maik klein via Digitalmars-d-learn wrote:
 On Sunday, 31 January 2016 at 17:42:19 UTC, anonymous wrote:
 On 31.01.2016 18:21, Matt Elkins wrote:
 I don't know if this works in all cases, but it passes that
 simple test:

 ----
  disable void foo(ref int x);
 void foo(int x) {}

 void main()
 {
     foo(5); /* works */
     int y = 5;
     foo(y); /* error */
 }
 ----
The problem is that x will be copied afaik which is not what you want if you want to deal with ownership.
In D, if you pass an rvalue to a function or assign it to a variable, then it's going to be moved, not copied. So by making it illegal to pass an lvalue to a function, you guarantee that every argument it gets was moved and not copied. - Jonathan M Davis
Jan 31 2016
prev sibling parent reply Matt Elkins <notreal fake.com> writes:
On Sunday, 31 January 2016 at 17:42:19 UTC, anonymous wrote:
 I don't know if this works in all cases, but it passes that 
 simple test:

 ----
  disable void foo(ref int x);
 void foo(int x) {}

 void main()
 {
     foo(5); /* works */
     int y = 5;
     foo(y); /* error */
 }
 ----
My fault, I should have better explained the situation I'm running into. I've boiled it down to this: [code] struct Foo { disable this(this); disable void opAssign(ref Foo); void opAssign(Foo foo) {} } unittest { void bar(Foo foo) { Foo foo1; foo1 = foo; // Fails to compile here } Foo makeFoo() {return Foo();} bar(Foo()); } [/code] [output] Error: function Foo.opAssign is not callable because it is annotated with disable [/output] Note that if I don't declare and assign foo1 on separate steps it yells at me for the post-blit constructor being disabled, which is reasonable. But it seems like the rvalue assignment operator should work...
Jan 31 2016
parent reply Matt Elkins <notreal fake.com> writes:
Errr, ignore the makeFoo() line. Left that in by accident, has no 
bearing on the issue.
Jan 31 2016
next sibling parent Matt Elkins <notreal fake.com> writes:
On Sunday, 31 January 2016 at 17:55:53 UTC, Matt Elkins wrote:
 Errr, ignore the makeFoo() line. Left that in by accident, has 
 no bearing on the issue.
Ok, I think I understand why this doesn't work, at least. The Foo passed into bar() is, of course, an lvalue itself. So I can achieve this with a new bar(), like so: [code] void bar(Foo foo) { import std.algorithm.mutation; Foo foo1 = move(foo); } [/code]
Jan 31 2016
prev sibling parent maik klein <maikklein googlemail.com> writes:
On Sunday, 31 January 2016 at 17:55:53 UTC, Matt Elkins wrote:
 Errr, ignore the makeFoo() line. Left that in by accident, has 
 no bearing on the issue.
I have found an interesting SO answer http://stackoverflow.com/a/35114945/944430 This would explain everything that we would need. I am just not 100% sure if everything he says is actually true.
Jan 31 2016