www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Q: Populating a structure with RTTI

reply Myron Alexander <someone somewhere.com> writes:
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
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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
parent reply Myron Alexander <someone somewhere.com> writes:
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-Sauls

Thanks 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) ();
    
    writefln ("%s, %s, %s, %s, %s, %#.2f", x.name, x.age, x.addr1, x.addr2,
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
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Myron Alexander wrote:
       /+ Won't compile:
       for (int i = 0; i < T.tupleof.length; i++) {
          t.tupleof[i] = r.t[i];
       }
       +/


You can use foreach on tuples: foreach(index, value; r.t) t.tupleof[index] = value;
Jun 11 2007
next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Lutger wrote:
 Myron Alexander wrote:
       /+ Won't compile:
       for (int i = 0; i < T.tupleof.length; i++) {
          t.tupleof[i] = r.t[i];
       }
       +/


You can use foreach on tuples: foreach(index, value; r.t) t.tupleof[index] = value;

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);
Jun 11 2007
parent reply Myron Alexander <someone somewhere.com> writes:
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
parent Myron Alexander <someone somewhere.com> writes:
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) ();
    writefln ("%s, %s, %s, %s, %s, %#.2f", x.name, x.age, x.addr1, x.addr2,
x.addr3, x.balance);
 
    auto y = fetchstruc!(OtherValue) ();
    writefln ("%s, %s, %s", y.type, y.dirname, y.filename);
    
    auto a = fetchnullstruc!(ValueStrucWNull) ();
    writefln ("%s, %s, %s, %s, %s, %#.2f, %s", a.name, a.age, a.addr1, a.addr2,
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
prev sibling parent Myron Alexander <someone somewhere.com> writes:
Lutger wrote:
 Myron Alexander wrote:
       /+ Won't compile:
       for (int i = 0; i < T.tupleof.length; i++) {
          t.tupleof[i] = r.t[i];
       }
       +/


You can use foreach on tuples: foreach(index, value; r.t) t.tupleof[index] = value;

Thanks Lutger. One down, one to go. Regards, Myron. P.S. Here is an example using 2 structs:
 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) ();
    writefln ("%s, %s, %s, %s, %s, %#.2f", x.name, x.age, x.addr1, x.addr2,
x.addr3, x.balance);
 
    auto y = fetchstruc!(OtherValue) ();
    writefln ("%s, %s, %s", y.type, y.dirname, y.filename);
    
    auto a = fetchnullstruc!(ValueStrucWNull) ();
    writefln ("%s, %s, %s, %s, %s, %#.2f, %s", a.name, a.age, a.addr1, a.addr2,
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
prev sibling next sibling parent BLS <nanali nospam-wanadoo.fr> writes:
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
prev sibling parent reply BLS <nanali nospam-wanadoo.fr> writes:
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
// create object collection , in C# I would choose Arraylist
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
parent Myron Alexander <someone somewhere.com> writes:
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