Thread overview
Ref local variables?
Jan 08, 2012
Ben Davis
Jan 08, 2012
Trass3r
Jan 08, 2012
Trass3r
Jan 08, 2012
simendsjo
Jan 08, 2012
Ben Davis
Jan 08, 2012
Ben Davis
Jan 08, 2012
Simen Kjærås
Jan 09, 2012
bearophile
January 08, 2012
Hi,

Is there a reason 'ref' is disallowed for local variables? I want to write something like:

MapTile[] map;	// It's a struct

ref MapTile tile=map[y*w+x];
tile.id=something;
tile.isWall=true;

My actual case is more complicated, so inlining the expression everywhere would be messy. I can't use 'with' because I sometimes pass 'tile' to a function (which also takes it as a ref). I don't want to make it a class since the array is quite big and that would be a lot of extra overhead. For now I'm using pointers, but this is forcing me to insert & or * operators sometimes, and it also reduces the temptation to use 'ref' in the places where it IS allowed, since it's inconsistent.

I hope it's not a stupid question - it's my first one - but I couldn't find an answer anywhere. I like most of what I've seen of D so far, and I'm very glad to be able to leave C and C++ (mostly) behind!

Thanks,

Ben :)
January 08, 2012
> MapTile[] map;	// It's a struct
>
> ref MapTile tile=map[y*w+x];
> tile.id=something;
> tile.isWall=true;

MapTile* tile = &map[y*w+x];
January 08, 2012
Sorry, didn't read the rest. ^^
January 08, 2012
On 08.01.2012 18:54, Ben Davis wrote:
> Hi,
>
> Is there a reason 'ref' is disallowed for local variables? I want to
> write something like:
>
> MapTile[] map; // It's a struct
>
> ref MapTile tile=map[y*w+x];
> tile.id=something;
> tile.isWall=true;
>
> My actual case is more complicated, so inlining the expression
> everywhere would be messy. I can't use 'with' because I sometimes pass
> 'tile' to a function (which also takes it as a ref). I don't want to
> make it a class since the array is quite big and that would be a lot of
> extra overhead. For now I'm using pointers, but this is forcing me to
> insert & or * operators sometimes, and it also reduces the temptation to
> use 'ref' in the places where it IS allowed, since it's inconsistent.
>
> I hope it's not a stupid question - it's my first one - but I couldn't
> find an answer anywhere. I like most of what I've seen of D so far, and
> I'm very glad to be able to leave C and C++ (mostly) behind!
>
> Thanks,
>
> Ben :)

I got something working, but only when using templates. Take the following with a grain of salt as I'm a newbie myself.

struct MapTile {
    string id;
}

enum w = 80, h = 25;
MapTile[w*h] map;

ref MapTile getTile(int x, int y) {
    return map[y*w+x];
}

void f(T)(ref T tile) {
    tile.id = "f()";
}

void g(ref MapTile tile) {
    tile.id = "g()";
}

void main() {
    // You can use auto ref return to set values directly
    getTile(10,10).id = "a";
    assert(getTile(10,10).id == "a");

    // And using templated ref arguments, you can pass by reference
    // note that I need to take the reference even when
    // using auto ref return
    auto tile = &getTile(1,1);
    f(tile);
    assert(tile.id == "f()");

    // But you'll need a dereference if not using a template
    g(*tile);
    assert(tile.id == "g()");

    assert(getTile(1,1).id == "g()");
}

January 08, 2012
There are two things going on in your example:

1. Use of 'auto' where I'm currently having to write 'MapTile*' and wanted to write 'ref MapTile'. It will infer 'MapTile*'. Or, if you forget the &, then it will infer 'MapTile' and do the copy that I want to avoid. So it might be slightly more error-prone this way.

2. Use of a template function, allowing f() to take any type, not just MapTile. Interestingly you're passing MapTile*, AND the parameter is ref, so f() could change the pointer itself for the caller if it wanted to! Also, if you called f() elsewhere with just a MapTile, you'd have two copies if the function kicking around. So it saves us having to write the *, but at the expense of some messiness. (Also prevents a lot of the compile-time checks for f() from happening until f() is used.)

At least that's assuming D templates are like C++ ones. I haven't read up on D templates yet. :)

So unfortunately, neither solution really solves the problem.

I suspect 'ref' for local variables could be added, and hasn't because it didn't figure in the specific use cases that someone had in mind when implementing it for parameters and 'foreach' variables. But it's also possible that (unlike in Java) local variables can do stuff that parameters can't (which I don't know about), which makes it impossible to allow 'ref' ones without invalidating a compile-time check or something. Hence why I was asking. :)

Anyway, I hope I've been able to help a bit with your own D adventure :)

Ben :)

On 08/01/2012 19:27, simendsjo wrote:
> I got something working, but only when using templates. Take the
> following with a grain of salt as I'm a newbie myself.
>
> struct MapTile {
> string id;
> }
>
> enum w = 80, h = 25;
> MapTile[w*h] map;
>
> ref MapTile getTile(int x, int y) {
> return map[y*w+x];
> }
>
> void f(T)(ref T tile) {
> tile.id = "f()";
> }
>
> void g(ref MapTile tile) {
> tile.id = "g()";
> }
>
> void main() {
> // You can use auto ref return to set values directly
> getTile(10,10).id = "a";
> assert(getTile(10,10).id == "a");
>
> // And using templated ref arguments, you can pass by reference
> // note that I need to take the reference even when
> // using auto ref return
> auto tile = &getTile(1,1);
> f(tile);
> assert(tile.id == "f()");
>
> // But you'll need a dereference if not using a template
> g(*tile);
> assert(tile.id == "g()");
>
> assert(getTile(1,1).id == "g()");
> }
>

January 08, 2012
I also meant to say: 80x25? Epic :D

On 08/01/2012 20:25, Ben Davis wrote:
> There are two things going on in your example:
>
> 1. Use of 'auto' where I'm currently having to write 'MapTile*' and
> wanted to write 'ref MapTile'. It will infer 'MapTile*'. Or, if you
> forget the &, then it will infer 'MapTile' and do the copy that I want
> to avoid. So it might be slightly more error-prone this way.
>
> 2. Use of a template function, allowing f() to take any type, not just
> MapTile. Interestingly you're passing MapTile*, AND the parameter is
> ref, so f() could change the pointer itself for the caller if it wanted
> to! Also, if you called f() elsewhere with just a MapTile, you'd have
> two copies if the function kicking around. So it saves us having to
> write the *, but at the expense of some messiness. (Also prevents a lot
> of the compile-time checks for f() from happening until f() is used.)
>
> At least that's assuming D templates are like C++ ones. I haven't read
> up on D templates yet. :)
>
> So unfortunately, neither solution really solves the problem.
>
> I suspect 'ref' for local variables could be added, and hasn't because
> it didn't figure in the specific use cases that someone had in mind when
> implementing it for parameters and 'foreach' variables. But it's also
> possible that (unlike in Java) local variables can do stuff that
> parameters can't (which I don't know about), which makes it impossible
> to allow 'ref' ones without invalidating a compile-time check or
> something. Hence why I was asking. :)
>
> Anyway, I hope I've been able to help a bit with your own D adventure :)
>
> Ben :)
>
> On 08/01/2012 19:27, simendsjo wrote:
>> I got something working, but only when using templates. Take the
>> following with a grain of salt as I'm a newbie myself.
>>
>> struct MapTile {
>> string id;
>> }
>>
>> enum w = 80, h = 25;
>> MapTile[w*h] map;
>>
>> ref MapTile getTile(int x, int y) {
>> return map[y*w+x];
>> }
>>
>> void f(T)(ref T tile) {
>> tile.id = "f()";
>> }
>>
>> void g(ref MapTile tile) {
>> tile.id = "g()";
>> }
>>
>> void main() {
>> // You can use auto ref return to set values directly
>> getTile(10,10).id = "a";
>> assert(getTile(10,10).id == "a");
>>
>> // And using templated ref arguments, you can pass by reference
>> // note that I need to take the reference even when
>> // using auto ref return
>> auto tile = &getTile(1,1);
>> f(tile);
>> assert(tile.id == "f()");
>>
>> // But you'll need a dereference if not using a template
>> g(*tile);
>> assert(tile.id == "g()");
>>
>> assert(getTile(1,1).id == "g()");
>> }
>>
>

January 08, 2012
On Sun, 08 Jan 2012 18:54:13 +0100, Ben Davis <entheh@cantab.net> wrote:

> Hi,
>
> Is there a reason 'ref' is disallowed for local variables? I want to write something like:
>
> MapTile[] map;	// It's a struct
>
> ref MapTile tile=map[y*w+x];
> tile.id=something;
> tile.isWall=true;
>
> My actual case is more complicated, so inlining the expression everywhere would be messy. I can't use 'with' because I sometimes pass 'tile' to a function (which also takes it as a ref). I don't want to make it a class since the array is quite big and that would be a lot of extra overhead. For now I'm using pointers, but this is forcing me to insert & or * operators sometimes, and it also reduces the temptation to use 'ref' in the places where it IS allowed, since it's inconsistent.
>
> I hope it's not a stupid question - it's my first one - but I couldn't find an answer anywhere. I like most of what I've seen of D so far, and I'm very glad to be able to leave C and C++ (mostly) behind!
>
> Thanks,
>
> Ben :)

Quick hack:

struct Ref( T ) {
	private:
	T* data;
	public:
	this( ref T value ) {
		data = &value;
	}
	
	ref inout(T) get( ) inout {
		return *data;
	}
	
	alias get this;
}

Ref!T byRef( T )( ref T value ) {
	return Ref!T( value );
}

unittest {
	int a = 3;
	Ref!int b = a;
	b = 4;
	assert( a == 4 );
	
	auto c = byRef( a );
	c = 5;
	assert( a == 5 );
	assert( b == 5 );
}
January 09, 2012
On Sun, 08 Jan 2012 12:54:13 -0500, Ben Davis <entheh@cantab.net> wrote:

> Hi,
>
> Is there a reason 'ref' is disallowed for local variables? I want to write something like:
>
> MapTile[] map;	// It's a struct
>
> ref MapTile tile=map[y*w+x];
> tile.id=something;
> tile.isWall=true;
>
> My actual case is more complicated, so inlining the expression everywhere would be messy. I can't use 'with' because I sometimes pass 'tile' to a function (which also takes it as a ref). I don't want to make it a class since the array is quite big and that would be a lot of extra overhead. For now I'm using pointers, but this is forcing me to insert & or * operators sometimes, and it also reduces the temptation to use 'ref' in the places where it IS allowed, since it's inconsistent.
>
> I hope it's not a stupid question - it's my first one - but I couldn't find an answer anywhere. I like most of what I've seen of D so far, and I'm very glad to be able to leave C and C++ (mostly) behind!

My first inclination is to use pointers.  D doesn't have -> operator, so pointers are seamless for your small example:

auto tile = &map[y*w+x];
tile.id = something;
tile.isWall = true;

When you need to pass it to a ref function, or if you do any operators on it, you would need to use *.

Another horribly inefficient option (if you don't use -inline) is this:

@property ref tile() { return map[y*w+x]; }

With new => syntax (in git head), this would probably be:

@property ref tile => map[y*w+x];

-Steve
January 09, 2012
Steven Schveighoffer:

> With new => syntax (in git head), this would probably be:
> 
> @property ref tile => map[y*w+x];

That's not currently supported: http://d.puremagic.com/issues/show_bug.cgi?id=7176

Bye,
bearophile
January 09, 2012
On Mon, 09 Jan 2012 09:27:06 -0500, bearophile <bearophileHUGS@lycos.com> wrote:

> Steven Schveighoffer:
>
>> With new => syntax (in git head), this would probably be:
>>
>> @property ref tile => map[y*w+x];
>
> That's not currently supported:
> http://d.puremagic.com/issues/show_bug.cgi?id=7176

The given function is not a method, but it is an inner-class function.

But you are right, it's a named function not a lambda.  I'm not too concerned about whether this version works or not, the other version is reasonably short.

I wonder if this would work, but it wouldn't be inlined like my original example:

auto tile = @property ref () => map[y*w+x];

-Steve