digitalmars.D.learn - Q: Populating a structure with RTTI
- Myron Alexander (23/23) Jun 10 2007 Hello.
- Chris Nicholson-Sauls (10/43) Jun 10 2007 T populate (T) () {
- Myron Alexander (12/101) Jun 11 2007 Thanks Chris.
- Lutger (4/9) Jun 11 2007 You can use foreach on tuples:
- Lutger (21/32) Jun 11 2007 I missed the other one, this could be done in a similar fashion, for
- Myron Alexander (22/49) Jun 11 2007 Thanks. I can't make use of that as the fetch is a generic fetch from a
- Myron Alexander (10/202) Jun 11 2007 I received the answer in another thread so I have a functional spike.
- Myron Alexander (5/156) Jun 11 2007 Thanks Lutger. One down, one to go.
- BLS (11/44) Jun 11 2007 Hi Myron,
- BLS (52/85) Jun 11 2007 Hi Myron, I think my previous suggestion will not work; (compiletime iss...
- Myron Alexander (12/13) Jun 11 2007 Hello Bjoern.
Hello. Is it possible to populate a struct using RTTI? Example: Say I have a struct as such: struct Example { int x; int y; char[] z; } and I want to create and populate the structure from a function: T populate(T) () { T t; t.field[0] = 1; t.field[1] = 2; t.field[2] = "testing"; return t; } void main() { auto x = populate!(Example)(); } Is this possible? If so, what is the syntax? Thanks ahead, Myron.
Jun 10 2007
Myron Alexander wrote:Hello. Is it possible to populate a struct using RTTI? Example: Say I have a struct as such: struct Example { int x; int y; char[] z; } and I want to create and populate the structure from a function: T populate(T) () { T t; t.field[0] = 1; t.field[1] = 2; t.field[2] = "testing"; return t; } void main() { auto x = populate!(Example)(); } Is this possible? If so, what is the syntax? Thanks ahead, Myron.T populate (T) () { T t; t.tupleof[0] = 1; t.tupleof[1] = 2; t.tupleof[2] = "testing"; return t; } Although its dangerous, but I'm sure you knew that already. -- Chris Nicholson-Sauls
Jun 10 2007
Chris Nicholson-Sauls wrote:T populate (T) () { T t; t.tupleof[0] = 1; t.tupleof[1] = 2; t.tupleof[2] = "testing"; return t; } Although its dangerous, but I'm sure you knew that already. -- Chris Nicholson-SaulsThanks Chris. I do have another problem that you may be able to help me with. Here is an example of how I would use the above:import std.stdio; import std.traits; /* Example value object. */ struct ValueStruc { char[] name; int age; char[] addr1; char[] addr2; char[] addr3; double balance; } /* Typesafe database row object. */ struct Row(T ...) { T t; bool[T.length] nullFlag; } /* Simulate a typesafe fetch from the database. */ Row!(T) fetchOne(T ...) () { Row!(T) r; /+ Won't compile: int i = 0; r.t[i++] = "Myron"; r.t[i++] = 10; r.t[i++] = "addr1"; r.t[i++] = "addr2"; r.t[i++] = "addr3"; r.t[i++] = 100001.10; +/ r.t[0] = "Myron"; r.t[1] = 10; r.t[2] = "addr1"; r.t[3] = "addr2"; r.t[4] = "addr3"; r.t[5] = 100001.10; r.nullFlag[0] = false; r.nullFlag[1] = true; r.nullFlag[2] = false; return r; } /* Fetch a typesafe record from the database and populate the struct. */ T fetchStruc(T) () { static if (is (T == struct)) { auto r = fetchOne!(FieldTypeTuple!(T))(); T t; /+ Won't compile: for (int i = 0; i < T.tupleof.length; i++) { t.tupleof[i] = r.t[i]; } +/ // My nasty hack to get it to work: static if ( 0 < T.tupleof.length) t.tupleof[0] = r.t[0]; static if ( 1 < T.tupleof.length) t.tupleof[1] = r.t[1]; static if ( 2 < T.tupleof.length) t.tupleof[2] = r.t[2]; static if ( 3 < T.tupleof.length) t.tupleof[3] = r.t[3]; static if ( 4 < T.tupleof.length) t.tupleof[4] = r.t[4]; static if ( 5 < T.tupleof.length) t.tupleof[5] = r.t[5]; return t; } else { static assert (0); } } void main () { auto x = fetchStruc!(ValueStruc) (); x.addr3, x.balance); }I would like to implement the "Won't compile" blocks but have no clue if it is even possible. I'm thinking either a mixin, or a compile time function or something to that effect would allow me to build the functions so I don't have to use a whole whackload of static ifs as I have done in the fetchStruc. If I can get this right, would make my normal use-case much easier. Thanks, Myron.
Jun 11 2007
Myron Alexander wrote:You can use foreach on tuples: foreach(index, value; r.t) t.tupleof[index] = value;/+ Won't compile: for (int i = 0; i < T.tupleof.length; i++) { t.tupleof[i] = r.t[i]; } +/
Jun 11 2007
Lutger wrote:Myron Alexander wrote:I missed the other one, this could be done in a similar fashion, for example: /+ Won't compile: int i = 0; r.t[i++] = "Myron"; r.t[i++] = 10; r.t[i++] = "addr1"; r.t[i++] = "addr2"; r.t[i++] = "addr3"; r.t[i++] = 100001.10; +/ with a helper function: void populateRow(R, T...)(inout R r, T t) { static assert(is(typeof(r.t) == T)); foreach(index, value; t) r.t[index] = value; } inside fetchOne: populateRow(r, "Myron"[], 10, "addr1"[], "addr2"[], "addr3"[], 100001.10);You can use foreach on tuples: foreach(index, value; r.t) t.tupleof[index] = value;/+ Won't compile: for (int i = 0; i < T.tupleof.length; i++) { t.tupleof[i] = r.t[i]; } +/
Jun 11 2007
Lutger wrote:I missed the other one, this could be done in a similar fashion, for example: /+ Won't compile: int i = 0; r.t[i++] = "Myron"; r.t[i++] = 10; r.t[i++] = "addr1"; r.t[i++] = "addr2"; r.t[i++] = "addr3"; r.t[i++] = 100001.10; +/ with a helper function: void populateRow(R, T...)(inout R r, T t) { static assert(is(typeof(r.t) == T)); foreach(index, value; t) r.t[index] = value; } inside fetchOne: populateRow(r, "Myron"[], 10, "addr1"[], "addr2"[], "addr3"[], 100001.10);Thanks. I can't make use of that as the fetch is a generic fetch from a database so it does not know the number of parameters upfront. This is an example of what I would like to do: for (int i = 0; i < r.t.length; i++) { getByType (i, r.t[i]); } Where getByType is a method on the statement object that get's the i'th column and sets r.t[i]. getByType (int i, ref int value); getByType (int i, ref long value); getByType (int i, ref char[] value); ... That is just a paradigm example, it could also be written using: for (int i = 0; i < r.t.length; i++) { if (r.t[i].type = typeid (int) { r.t[i] = getInt (i); } else if ... } Any ideas? Thanks, Myron.
Jun 11 2007
I received the answer in another thread so I have a functional spike. I just want to thank Chris Nicholson-Sauls, Lutger Blijdestijn, and Christian Kam for the help. Without you guys this would not have been possible. I am so excited now and if the concept pans out, I think I may explode :) Just one last thing, what do you think of the type-safe fetch idea, something that will make your lives easier, or just another WTF? Best Regards, Myron. Here is the final spike in full:import std.stdio; import std.traits; import std.conv; /* Example value object. */ struct ValueStruc { char[] name; int age; char[] addr1; char[] addr2; char[] addr3; double balance; } struct OtherValue { int type; char[] dirname; char[] filename; } struct ValueStrucWNull { char[] name; int age; char[] addr1; char[] addr2; char[] addr3; double balance; bool[6] nullFlag; } struct OtherValueWNull { int type; char[] dirname; char[] filename; bool[3] nullFlag; } /* Typesafe database row object. */ struct Row(T ...) { T t; bool[T.length] nullFlag; } char[][] args; int getInt (int i) { return toInt (args[i+1]); } char[] getString (int i) { return args[i+1]; } double getDouble (int i) { return toDouble(args[i+1]); } // This works T getRowElement(T) (uint i) { static if (is (T == int)) { return getInt (i); } else static if (is (T == char[])) { return getString (i); } else static if (is (T == double)) { return getDouble (i); } else { static assert (0); } } // So do these when the index is uint. If you use void getElem (int i, ..... // then it fails as it does not know how to differentiate between "int value" // and "double value" signatures. void getElem (uint i, ref int value) { value = getInt (i); } void getElem (uint i, ref char[] value) { value = getString (i); } void getElem (uint i, ref double value) { value = getDouble (i); } /* Simulate a typesafe fetch from the database. * * With thanks to Chris Nicholson-Sauls, Lutger Blijdestijn, and Christian Kam * who provided code and information on how to make this possible. * * Thanks to Kirk McDonald for for the type-safe fetch idea. */ Row!(T) fetchone(T ...) () { Row!(T) r; foreach (i, val; r.t) { r.t[i] = getRowElement!(typeof(r.t[i]))(i); // Works too //getElem (i, r.t[i]); } static if (T.length == 6) { //~ r.t[0] = "Myron"; //~ r.t[1] = 10; //~ r.t[2] = "addr1"; //~ r.t[3] = "addr2"; //~ r.t[4] = "addr3"; //~ r.t[5] = 100001.10; r.nullFlag[0] = true; r.nullFlag[1] = false; r.nullFlag[2] = false; r.nullFlag[3] = true; r.nullFlag[4] = false; r.nullFlag[5] = false; } static if (T.length == 3) { //~ r.t [0] = 34; //~ r.t [1] = r"c:\dir1\dir2\dir2\"; //~ r.t [2] = "filename"; r.nullFlag[0] = false; r.nullFlag[1] = true; r.nullFlag[2] = false; } return r; } /* Fetch a typesafe record from the database and populate the struct. * * With thanks to Chris Nicholson-Sauls, Lutger Blijdestijn, and Christian Kam. */ T fetchstruc(T) () { static if (is (T == struct)) { auto r = fetchone!(FieldTypeTuple!(T))(); T t; foreach (i, v; r.t) { // According to Christian, using v may not work every time. t.tupleof[i] = r.t[i]; } return t; } else { static assert (0); } } /* Fetch a typesafe record from the database, populate the struct and set * null field flags. */ T fetchnullstruc(T) () { static if (is (T == struct)) { auto r = fetchone!(FieldTypeTuple!(T)[0..length-1])(); T t; foreach (i, v; r.t) { // According to Christian, using v may not work every time. t.tupleof[i] = r.t[i]; } foreach (i, nullFlagValue; r.nullFlag) { t.nullFlag[i] = nullFlagValue; } return t; } else { static assert (0); } } void main (char[][] args) { .args = args; auto x = fetchstruc!(ValueStruc) (); x.addr3, x.balance); auto y = fetchstruc!(OtherValue) (); writefln ("%s, %s, %s", y.type, y.dirname, y.filename); auto a = fetchnullstruc!(ValueStrucWNull) (); a.addr3, a.balance, a.nullFlag); auto b = fetchnullstruc!(OtherValueWNull) (); writefln ("%s, %s, %s, %s", b.type, b.dirname, b.filename, b.nullFlag); }
Jun 11 2007
Lutger wrote:Myron Alexander wrote:Thanks Lutger. One down, one to go. Regards, Myron. P.S. Here is an example using 2 structs:You can use foreach on tuples: foreach(index, value; r.t) t.tupleof[index] = value;/+ Won't compile: for (int i = 0; i < T.tupleof.length; i++) { t.tupleof[i] = r.t[i]; } +/import std.stdio; import std.traits; /* Example value object. */ struct ValueStruc { char[] name; int age; char[] addr1; char[] addr2; char[] addr3; double balance; } struct OtherValue { int type; char[] dirname; char[] filename; } struct ValueStrucWNull { char[] name; int age; char[] addr1; char[] addr2; char[] addr3; double balance; bool[6] nullFlag; } struct OtherValueWNull { int type; char[] dirname; char[] filename; bool[3] nullFlag; } /* Typesafe database row object. */ struct Row(T ...) { T t; bool[T.length] nullFlag; } /* Simulate a typesafe fetch from the database. */ Row!(T) fetchone(T ...) () { Row!(T) r; /+ --- Won't compile -------------------------------------------------- int i = 0; r.t[i++] = "Myron"; r.t[i++] = 10; r.t[i++] = "addr1"; r.t[i++] = "addr2"; r.t[i++] = "addr3"; r.t[i++] = 100001.10; ----------------------------------------------------------------------- +/ static if (T.length == 6) { r.t[0] = "Myron"; r.t[1] = 10; r.t[2] = "addr1"; r.t[3] = "addr2"; r.t[4] = "addr3"; r.t[5] = 100001.10; r.nullFlag[0] = true; r.nullFlag[1] = false; r.nullFlag[2] = false; r.nullFlag[3] = true; r.nullFlag[4] = false; r.nullFlag[5] = false; } static if (T.length == 3) { r.t [0] = 34; r.t [1] = r"c:\dir1\dir2\dir2\"; r.t [2] = "filename"; r.nullFlag[0] = false; r.nullFlag[1] = true; r.nullFlag[2] = false; } return r; } /* Fetch a typesafe record from the database and populate the struct. */ T fetchstruc(T) () { static if (is (T == struct)) { auto r = fetchone!(FieldTypeTuple!(T))(); T t; foreach (i, v; r.t) { t.tupleof[i] = v; } return t; } else { static assert (0); } } /* Fetch a typesafe record from the database, populate the struct and set * null field flags. */ T fetchnullstruc(T) () { static if (is (T == struct)) { auto r = fetchone!(FieldTypeTuple!(T)[0..length-1])(); T t; foreach (i, v; r.t) { t.tupleof[i] = v; } foreach (i, nullFlagValue; r.nullFlag) { t.nullFlag[i] = nullFlagValue; } return t; } else { static assert (0); } } void main () { auto x = fetchstruc!(ValueStruc) (); x.addr3, x.balance); auto y = fetchstruc!(OtherValue) (); writefln ("%s, %s, %s", y.type, y.dirname, y.filename); auto a = fetchnullstruc!(ValueStrucWNull) (); a.addr3, a.balance, a.nullFlag); auto b = fetchnullstruc!(OtherValueWNull) (); writefln ("%s, %s, %s, %s", b.type, b.dirname, b.filename, b.nullFlag); }
Jun 11 2007
Hi Myron, I am also thinking about this problem for quit a while. Maybe you can use the D Mixin feature and compile time manipulation of strings to create an database-table adequate structure : template GenStruct(char[] Name, char[] M1) { const char[] GenStruct = "struct " ~ Name ~ "{ int " ~ M1 ~ "; }"; } mixin(GenStruct!("Foo", "bar")); Just an idea, Bjoern Myron Alexander schrieb:Hello. Is it possible to populate a struct using RTTI? Example: Say I have a struct as such: struct Example { int x; int y; char[] z; } and I want to create and populate the structure from a function: T populate(T) () { T t; t.field[0] = 1; t.field[1] = 2; t.field[2] = "testing"; return t; } void main() { auto x = populate!(Example)(); } Is this possible? If so, what is the syntax? Thanks ahead, Myron.
Jun 11 2007
Hi Myron, I think my previous suggestion will not work; (compiletime issue) However, I guess your question is regarding the database row[][] problem, or how to create an adequate datatype for an database-table at compile time. I would like to suggest to have a look on this solution (runtime) pseudo code first : program startup connecttodb def tablerow as Arraylist // browse system tables foreach table in db addTableInformation // name f.i. foreach tablerow in table // create an object which is adequate to the row datatyp // to do this use the /factory pattern/, see link below // next transfer rowinformation into this object add this object to tablerow end end So now you have a chained list of objects, where each object represents one table-row (or table-field, if you like) In D I would choose an associative array containing the tablename as index and the Arraylist as value; factory pattern link : http://www.dofactory.com/Patterns/PatternAbstract.aspx probabaly you prefer the prototyp pattern; You will find information regarding this pattern on the same site. Regarding the Arraylist, Tango has to offer a lot of collection classes, I guess ArraySeq is what you need, but I am not sure, so you have to ask Sean. your row class can look like this class row private: string rowname string rowtype boolean primeryKey .... end your concrete row class may be class varcharRow inherits row // alias varchar char[] private: varchar value // the row value ... end // So if your database-table-row type is varchar, we will add an //instance of varcharRow to Arraylist. Just a quick hack but hopefully I was able to figure out the idea; Some feedback would be nice; Bjoern Myron Alexander schrieb:Hello. Is it possible to populate a struct using RTTI? Example: Say I have a struct as such: struct Example { int x; int y; char[] z; } and I want to create and populate the structure from a function: T populate(T) () { T t; t.field[0] = 1; t.field[1] = 2; t.field[2] = "testing"; return t; } void main() { auto x = populate!(Example)(); } Is this possible? If so, what is the syntax? Thanks ahead, Myron.
Jun 11 2007
BLS wrote:Hi Myron, I think my previous suggestion will not work; (compiletime issue)Hello Bjoern. Thanks for the suggestion. I am hoping to build what I call a type-safe fetch mechanism so constructing the template at compile time is not a problem. For an example of what I am hoping to achieve, take a look at my response to Chris. If I can get that right, I will be extremely happy. I currently have a "generic" mechanism using Box arrays and I am trying to keep my implementation as "simple" as possible so the use of DAO type factories does not fit into the theme. All the best, Myron.
Jun 11 2007