www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - how to access struct member using [] operator?

reply grampus <grampusluan hotmail.com> writes:
Dear all

For example, I have a struct
struct point{int x;int y}
point a;

Is there an easy way to access x and y by using a["x"] and a["y"]

I guess I need to overload [], but can't figure out how.

Someone can help? Thank you very much
Sep 24 2016
next sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
The way you access the memers of a struct is through the dot syntax. i.e. a.x = a.y;
 Is there an easy way to access x and y by using a["x"] and 
 a["y"]
in this case "x" and "y" are strings so no.
Sep 24 2016
prev sibling next sibling parent Basile B. <b2.temp gmx.com> writes:
On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
You can't do that. There's a problem because the string passed is not known at compile-time, so the return type of "opIndex(string member)" cannot be inferred. It works only for one type: °°°°°°°°°°°°°°°°°°° import std.stdio; struct Something { int x,y; float z; int opIndex(string member) { alias T = typeof(this); foreach(m;__traits(allMembers, T)) if (m == member) { static if (is(typeof(__traits(getMember, T, m)) == int)) return __traits(getMember, T, m); } assert(0); } } void main(string[] args) { Something s; writeln(s["x"]); } °°°°°°°°°°°°°°°°°°° If you add a template parameter to opIndex then you can't call it with the array syntax so it becomes pointless to use an operator overload.
Sep 25 2016
prev sibling next sibling parent reply Namespace <rswhite4 gmail.com> writes:
On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
---- import std.stdio; struct Something { int x, y; float z; auto opIndex()(string member) { switch (member) { case "x": return this.x; case "y": return this.y; case "z": return this.z; default: assert(0); } } } void main(string[] args) { Something s; writeln(s["x"]); writeln(s["z"]); } ----
Sep 25 2016
next sibling parent Lodovico Giaretta <lodovico giaretart.net> writes:
On Sunday, 25 September 2016 at 09:01:44 UTC, Namespace wrote:
 ----
 import std.stdio;

 struct Something
 {
     int x, y;
     float z;

     auto opIndex()(string member) {
 		switch (member) {
 			case "x": return this.x;
 			case "y": return this.y;
 			case "z": return this.z;
 			default: assert(0);
 		}
     }
 }

 void main(string[] args)
 {
     Something s;
     writeln(s["x"]);
     writeln(s["z"]);
 }
 ----
Doesn't work. s["x"] is returned as float in this example. The reason is, opIndex cannot magically change return type based on the passed-in string.
Sep 25 2016
prev sibling next sibling parent grampus <grampusluan hotmail.com> writes:
On Sunday, 25 September 2016 at 09:01:44 UTC, Namespace wrote:
 On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
---- import std.stdio; struct Something { int x, y; float z; auto opIndex()(string member) { switch (member) { case "x": return this.x; case "y": return this.y; case "z": return this.z; default: assert(0); } } } void main(string[] args) { Something s; writeln(s["x"]); writeln(s["z"]); } ----
I think this approach should work for me. Thank you Namespace:)
Sep 25 2016
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Sunday, 25 September 2016 at 09:01:44 UTC, Namespace wrote:
 On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
---- import std.stdio; struct Something { int x, y; float z; auto opIndex()(string member) { switch (member) { case "x": return this.x; case "y": return this.y; case "z": return this.z; default: assert(0); } } } void main(string[] args) { Something s; writeln(s["x"]); writeln(s["z"]); } ----
WooW I have to say that I'm mesmerized ! How can this works ? "member" is run time variable so the return type shouldn't be inferable.
Sep 25 2016
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Sunday, 25 September 2016 at 16:07:59 UTC, Basile B. wrote:
 On Sunday, 25 September 2016 at 09:01:44 UTC, Namespace wrote:
 On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
---- import std.stdio; struct Something { int x, y; float z; auto opIndex()(string member) { switch (member) { case "x": return this.x; case "y": return this.y; case "z": return this.z; default: assert(0); } } } void main(string[] args) { Something s; writeln(s["x"]); writeln(s["z"]); } ----
WooW I have to say that I'm mesmerized ! How can this works ? "member" is run time variable so the return type shouldn't be inferable.
Ther's no trick related to compile time: import std.stdio; struct Something { int y; float z; double e; float x = 1234; auto opIndex()(const(char)[] member) { switch (member) { case "x": return this.x; case "y": return this.y; case "z": return this.z; case "e": return this.e; default: assert(0); } } } void main(string[] args) { Something s; char[] member = "w".dup; member[0] += args.length; writeln(s[member]); } Can we get an explanation from a compiler guy ? It seems the the switch statement is already evaluated at compiled time...
Sep 25 2016
next sibling parent Basile B. <b2.temp gmx.com> writes:
On Sunday, 25 September 2016 at 16:26:11 UTC, Basile B. wrote:
 On Sunday, 25 September 2016 at 16:07:59 UTC, Basile B. wrote:
 On Sunday, 25 September 2016 at 09:01:44 UTC, Namespace wrote:
 On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
---- import std.stdio; struct Something { int x, y; float z; auto opIndex()(string member) { switch (member) { case "x": return this.x; case "y": return this.y; case "z": return this.z; default: assert(0); } } } void main(string[] args) { Something s; writeln(s["x"]); writeln(s["z"]); } ----
WooW I have to say that I'm mesmerized ! How can this works ? "member" is run time variable so the return type shouldn't be inferable.
Ther's no trick related to compile time: import std.stdio; struct Something { int y; float z; double e; float x = 1234; auto opIndex()(const(char)[] member) { switch (member) { case "x": return this.x; case "y": return this.y; case "z": return this.z; case "e": return this.e; default: assert(0); } } } void main(string[] args) { Something s; char[] member = "w".dup; member[0] += args.length; writeln(s[member]); } Can we get an explanation from a compiler guy ? It seems the the switch statement is already evaluated at compiled time...
"return" in "case" can infer...TIL. That's still a bit strange from the ABI point of view...because it means that under win32 it knows that z and x have to be returned in ST0, y in EAX...but cases are known at compile time...ok I think I get it :]. Depending on a static case, another CPU register is filled...
Sep 25 2016
prev sibling parent ag0aep6g <anonymous example.com> writes:
On 09/25/2016 06:26 PM, Basile B. wrote:
 Can we get an explanation from a compiler guy ? It seems the the switch
 statement is already evaluated at compiled time...
Lodovico has already answered this. It's just an ordinary `auto` return type function. The actual return type is the common type of the different returns. Note that it falls apart when you add member with an incompatible type, like `string` or `Object`.
Sep 25 2016
prev sibling parent pineapple <meapineapple gmail.com> writes:
On Sunday, 25 September 2016 at 16:07:59 UTC, Basile B. wrote:
 WooW I have to say that I'm mesmerized !

 How can this works ? "member" is run time variable so the 
 return type shouldn't be inferable.
The int fields are promoted to and returned as floats.
Sep 26 2016
prev sibling next sibling parent reply pineapple <meapineapple gmail.com> writes:
On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
If they all share the same type, you can use a switch like Namespace suggested. If the "x" and "y" strings are available at compile-time, you can use a mixin. auto getattr(string attr)(point a){ mixin(`return a.` ~ attr ~ `;); } auto x = a.attr!"x"; Otherwise, no. D types aren't dynamic in the same way that Python's types are.
Sep 25 2016
parent grampus <grampusluan hotmail.com> writes:
On Sunday, 25 September 2016 at 10:44:38 UTC, pineapple wrote:
 On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
If they all share the same type, you can use a switch like Namespace suggested. If the "x" and "y" strings are available at compile-time, you can use a mixin. auto getattr(string attr)(point a){ mixin(`return a.` ~ attr ~ `;); } auto x = a.attr!"x"; Otherwise, no. D types aren't dynamic in the same way that Python's types are.
Thank you all for the clear reply. Now I know how far I can go on this.
Sep 25 2016
prev sibling parent ZombineDev <petar.p.kirov gmail.com> writes:
On Sunday, 25 September 2016 at 04:54:31 UTC, grampus wrote:
 Dear all

 For example, I have a struct
 struct point{int x;int y}
 point a;

 Is there an easy way to access x and y by using a["x"] and 
 a["y"]

 I guess I need to overload [], but can't figure out how.

 Someone can help? Thank you very much
Here's my solution. It's very similar to Namespace's solution, the main difference being that it doesn't coerce the result to the common type of all the fields which may not be always possible and is also looses information. It also uses this.tupleof and foreach, so you don't have to write the switch cases by hand. ``` struct Something { int x, y; float z; string w; auto opIndex(string member) { import std.variant : Algebraic; import std.traits : Fields; alias FieldTypes = Fields!(typeof(this)); alias CommonType = Algebraic!(FieldTypes, typeof(null)); switch (member) { foreach (idx, field; this.tupleof) { case this.tupleof[idx].stringof[5 .. $]: return CommonType(this.tupleof[idx]); } default: assert (0, "Type `" ~ typeof(this).stringof ~ "` doesn't contain any member named `" ~ member ~ "`!"); // or return CommonType(null); } } } void main() { import std.stdio; auto s = Something(3, 4, 4.5, "asd"); writeln(s["x"]); writeln(s["y"]); writeln(s["z"]); writeln(s["w"]); writeln(s["missing_field"]); } ``` Application output: 3 4 4.5 asd Application error: core.exception.AssertError /home/d623/f162.d(26): Type `Something` doesn't contain any member named `missing_field`! The [5 .. $] trick is needed, because for e.g. idx = 0, this.tupleof[0].stringof returns "this.x" and we want to skip the "this." part (because otherwise the user would need to type s["this.x"]).
Sep 26 2016