Thread overview | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
November 08, 2013 Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
I have been playing around with a vector implementation that I am trying to write in D, and have been having problems getting something to work (it probably isn't even possible). My end goal is to be able to instantiate a vector with a syntax like... `Vector!(2, float) vec = new Vector!(2, float)();` Now this part I can get working by having `class Vector(int i, T)` and an array such as `T[i] data`. The thing that I have been trying to do, is allow access to the components of the vector through properties that are identified by letter. Ideally it would work like `vec.x = 4.0f;` or something like that. I have tried using @property and the opDispatch method together, but that does not compile. I know I can just use array indicies to access components, but first I wanted to see if this was possible. Any suggestions? Thank you, Ross Hays |
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ross Hays | On Friday, 8 November 2013 at 02:13:01 UTC, Ross Hays wrote:
> My end goal is to be able to instantiate a vector with a syntax
> like...
> `Vector!(2, float) vec = new Vector!(2, float)();`
>
> ...
>
> Any suggestions?
Greetings,
This works:
---
import std.stdio;
struct Vector(int N, T) if (N <= 3) {
private T[N] data;
public @property
void opDispatch(string fieldName, Args ...)(Args args) if (Args.length == 1 && fieldName.length == 1 && cast(size_t)(fieldName[0] - 'x') < N) {
static immutable offset = cast(size_t)(fieldName[0] - 'x');
data[offset] = args[0];
}
public @property
T opDispatch(string fieldName, Args ...)(Args args) if (Args.length == 0 && fieldName.length == 1 && cast(size_t)(fieldName[0] - 'x') < N) {
static immutable offset = cast(size_t)(fieldName[0] - 'x');
return data[offset];
}
}
void main() {
auto vec = Vector!(2, float)();
vec.x = 1.0;
vec.y = 2.0;
//vec.z = 3.0; // Doesn't compile, as expected for a Vector!(2, float);
writeln("vec.x = ", vec.x, " and vec.y = ", vec.y);
}
---
Minor tweaks might be necessary, but that should get you started.
|
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Cain | On Friday, 8 November 2013 at 02:48:31 UTC, Chris Cain wrote:
> Minor tweaks might be necessary, but that should get you started.
Actually, I refactored it a little bit to make it better (original code was just a bit too messy for my taste):
---
struct Vector(int N, T) if (N <= 3) {
private T[N] data;
private static size_t toOffset(string fieldName) {
return cast(size_t)(fieldName[0] - 'x');
}
public @property
auto opDispatch(string fieldName, Args ...)(Args args)
if (Args.length < 2
&& fieldName.length == 1
&& toOffset(fieldName) < N) {
static immutable offset = toOffset(fieldName);
static if(Args.length == 0) {
// getter
return data[offset];
} else {
// setter
data[offset] = args[0];
}
}
}
---
|
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Cain | Awesome that seems to do what I was going for. I had tried a similar approach with @property dispatch and the subtraction of 'x', but I had left out the static if and had the opDispatch returning a ref of the entry in the array (so there would just be the one @property still) but that resulted in "Error: no property 'x' for type 'test.Vector!(2, float).Vector'". This is interesting, though probably not a very safe way to handle vectors in the real world (even more so if they are going to be vectors of more than length 3). |
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ross Hays | I am actually a little curious why my original approach did not work at all. Using some of what you provided and some of what I had I get the following: import std.stdio; import std.string; class Vector(int N, T) if (N <= 3) { T[N] data; this() { data[] = 0; } @property ref T opDispatch(string fieldName, Args ...)(Args args) if (Args.length < 2 && fieldName.length == 1 && toOffset(fieldName) < N) { int offset = fieldName.charAt(0) - 'x'; return data[offset] = args[0]; } } void main() { Vector!(2, float) t = new Vector!(2,float)(); writeln(t.x); } Which still errors out with: Error: no property 'x' for type 'test.Vector!(2, float).Vector' So that is odd. |
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ross Hays | On Friday, 8 November 2013 at 03:35:34 UTC, Ross Hays wrote: > Awesome that seems to do what I was going for. I had tried a similar approach with @property dispatch and the subtraction of 'x', but I had left out the static if and had the opDispatch returning a ref of the entry in the array (so there would just be the one @property still) but that resulted in "Error: no property 'x' for type 'test.Vector!(2, float).Vector'". > > This is interesting, though probably not a very safe way to handle vectors in the real world (even more so if they are going to be vectors of more than length 3). Indeed. Presumably you could modify it to shift the starting letter (which is currently hardcoded as 'x') leftward as N grows bigger to allow it to support more. But really, you would probably be better to just use tuples. Technically a Vector is just a Tuple!(T, "x", T, "y") so you could probably do better by writing some metaprogramming to make one of those if you wanted a short-hand for it. Otherwise just doing "alias Vector2(T) = Tuple!(T, "x", T, "y");" would work great in 2.064.2, if I remember the syntax correctly. Then Vector2!float would work. Similarly Vector3 could be made and so on. http://dlang.org/phobos/std_typecons.html#.Tuple http://dlang.org/changelog.html#eponymous_template |
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ross Hays | On Friday, 8 November 2013 at 03:42:12 UTC, Ross Hays wrote:
> I am actually a little curious why my original approach did not work at all. Using some of what you provided and some of what I had I get the following:
>
> import std.stdio;
> import std.string;
>
> class Vector(int N, T) if (N <= 3) {
> T[N] data;
>
> this()
> {
> data[] = 0;
> }
>
> @property ref T opDispatch(string fieldName, Args ...)(Args args)
> if (Args.length < 2 && fieldName.length == 1 && toOffset(fieldName) < N)
> {
> int offset = fieldName.charAt(0) - 'x';
> return data[offset] = args[0];
> }
> }
>
> void main()
> {
> Vector!(2, float) t = new Vector!(2,float)();
> writeln(t.x);
> }
>
> Which still errors out with: Error: no property 'x' for type 'test.Vector!(2, float).Vector'
>
> So that is odd.
Strange. I'm getting a different error, but I'm still running 2.063.2.
The error I get is
`Error: cannot resolve type for t.opDispatch!("x")`
What version are you running?
In any case, the reason apparently is multifold:
1. Apparently the proper error message isn't shown when using the property notation. (I'd have to check to see if it happens in 2.064 ... might be a fixed bug)
2. `.charAt(0)` doesn't exist for D's strings. You can just use bracket notation to access the index.
3. When args is empty (as it will be for a getter, when you call) args[0] doesn't exist, so `Error: array index [0] is outside array bounds [0 .. 0]`
So fix 2 and 3 and it works for getting x. The reason I use a static if is to separate the cases where args has items and when it does not (since args[0] is invalid when args.length == 0), so that'll be necessary to get it to work.
|
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Cain | On Friday, 8 November 2013 at 04:06:22 UTC, Chris Cain wrote:
> So fix 2 and 3 and it works for getting x.
Also, define `toOffset` in the template constraint.
|
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Cain | > Strange. I'm getting a different error, but I'm still running 2.063.2. > The error I get is > `Error: cannot resolve type for t.opDispatch!("x")` > What version are you running? I just updated to 2.064.2 > In any case, the reason apparently is multifold: > 1. Apparently the proper error message isn't shown when using the property notation. (I'd have to check to see if it happens in 2.064 ... might be a fixed bug) > 2. `.charAt(0)` doesn't exist for D's strings. You can just use bracket notation to access the index. > 3. When args is empty (as it will be for a getter, when you call) args[0] doesn't exist, so `Error: array index [0] is outside array bounds [0 .. 0]` > > So fix 2 and 3 and it works for getting x. The reason I use a static if is to separate the cases where args has items and when it does not (since args[0] is invalid when args.length == 0), so that'll be necessary to get it to work. Okay 2 is fixed, leftovers of other languages in my mind. Also took care of 3 I think but I may still be missing something. Here is what I have now.. class Vector(int N, T) if (N <= 3) { T[N] data; this() { data[] = 0; } @property ref T opDispatch(string fieldName, Args ...)(Args args) if (Args.length < 2 && fieldName.length == 1 && toOffset(fieldName) < N) { int offset = fieldName[0 .. 1] - 'x'; if (args.length != 0) return data[offset]; else return data[offset] = args[0]; } } Same error. |
November 08, 2013 Re: Template class with dispatched properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ross Hays | On Friday, 8 November 2013 at 04:28:31 UTC, Ross Hays wrote:
>> Strange. I'm getting a different error, but I'm still running 2.063.2.
>> The error I get is
>> `Error: cannot resolve type for t.opDispatch!("x")`
>> What version are you running?
>
> I just updated to 2.064.2
>
>> In any case, the reason apparently is multifold:
>> 1. Apparently the proper error message isn't shown when using the property notation. (I'd have to check to see if it happens in 2.064 ... might be a fixed bug)
>> 2. `.charAt(0)` doesn't exist for D's strings. You can just use bracket notation to access the index.
>> 3. When args is empty (as it will be for a getter, when you call) args[0] doesn't exist, so `Error: array index [0] is outside array bounds [0 .. 0]`
>>
>> So fix 2 and 3 and it works for getting x. The reason I use a static if is to separate the cases where args has items and when it does not (since args[0] is invalid when args.length == 0), so that'll be necessary to get it to work.
>
> Okay 2 is fixed, leftovers of other languages in my mind.
> Also took care of 3 I think but I may still be missing something.
>
> Here is what I have now..
>
>
> class Vector(int N, T) if (N <= 3) {
> T[N] data;
>
> this()
> {
> data[] = 0;
> }
>
> @property ref T opDispatch(string fieldName, Args ...)(Args args)
> if (Args.length < 2 && fieldName.length == 1 && toOffset(fieldName) < N)
> {
> int offset = fieldName[0 .. 1] - 'x';
> if (args.length != 0)
> return data[offset];
> else
> return data[offset] = args[0];
> }
> }
>
> Same error.
Just reread and realized I glossed over your mention of static if.
class Vector(int N, T) if (N <= 3) {
T[N] data;
this()
{
data[] = 0;
}
@property ref T opDispatch(string fieldName, Args ...)(Args args)
if (Args.length < 2 && fieldName.length == 1 && toOffset(fieldName) < N)
{
int offset = fieldName[0 .. 1] - 'x';
static if (args.length != 0)
return data[offset];
else
return data[offset] = args[0];
}
}
Still the same problems.
|
Copyright © 1999-2021 by the D Language Foundation