Jump to page: 1 2
Thread overview
Declaring rvalue function arguments
Jan 31, 2016
Matt Elkins
Jan 31, 2016
maik klein
Jan 31, 2016
anonymous
Jan 31, 2016
maik klein
Jan 31, 2016
Matt Elkins
Jan 31, 2016
Matt Elkins
Feb 01, 2016
Jonathan M Davis
Jan 31, 2016
Matt Elkins
Jan 31, 2016
Matt Elkins
Jan 31, 2016
Matt Elkins
Jan 31, 2016
maik klein
January 31, 2016
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.
January 31, 2016
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


January 31, 2016
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 */
}
----
January 31, 2016
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.
January 31, 2016
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...
January 31, 2016
Errr, ignore the makeFoo() line. Left that in by accident, has no bearing on the issue.
January 31, 2016
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]
January 31, 2016
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.
January 31, 2016
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]
January 31, 2016
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]
« First   ‹ Prev
1 2