www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - What is the "right" way to create a generic type getter (and setter) ?

reply James Blachly <james.blachly gmail.com> writes:
For context, please keep in mind I am coming from a python 
background, but am very much enjoying strong typing, although it 
is taking some significant adjustment.

Suppose I have a struct (which is really a memory map of a data 
file I am reading in) with too many data members to reasonably 
code getters/setters for by hand.  I wish to either retrieve 
individual values or set individual values, which could be 
numeric, boolean, or string, from the command line, à la:

$ prog -i inputfile.bin get field_name;
(prints "300" or "false" or "Welcome to the jungle")

$ prog -i inputfile.bin set some_field:9000
$ prog -i inputfile.bin set other_field:Whatever_String

Each field itself is strongly typed, for what that's worth.

Approaches I have considered and implemented in part are:
     * templated getter (T get(T)(string field) {...}) but this 
approach requires knowledge of field types which I cannot 
reasonably expect to know at runtime(?)
     *  modification to the above whereby I could have an AA 
holding type information for each field, generated by static 
foreach {mixin ...}, although I cannot get this to work as my 
struct's static constructor complains (rightly) that it cannot 
work without knowing 'this' at compile time. Code: 
`mixin("field_types[\"" ~ prop ~ "\"] = typeid(this." ~ prop ~ 
");");`  Is there another __trait I am missing that will give me 
the type of the struct member without requiring an instance of 
the struct?

I did manage to use metaprogramming inside my templated get 
function to handle numeric values, which was fascinating  
(although this is probably ugly code and it required a large enum 
array FIELDS):

```
         GetterSwitch:
         switch (field)
         {
             static foreach(prop; FIELDS ) {
                 mixin("case \"" ~ prop ~ "\": val = this." ~ prop 
~ "; break GetterSwitch;");
             }
             default:
                 val = 0;
                 assert(0);  // This is to prevent subtle bugs, 
but I need a better error handler
         }
```


Any pointers / design patterns on this particular type of problem 
class would be greatly appreciated.  (Sidenote, I realize I could 
probably use the witchcraft library, but I am also using this as 
exercise to learn D beyond the basics).

Thanks in advance
James
Mar 14 2018
parent reply ag0aep6g <anonymous example.com> writes:
On 03/14/2018 11:13 PM, James Blachly wrote:
 Suppose I have a struct (which is really a memory map of a data file I 
 am reading in) with too many data members to reasonably code 
 getters/setters for by hand.  I wish to either retrieve individual 
 values or set individual values, which could be numeric, boolean, or 
 string, from the command line, à la:
 
 $ prog -i inputfile.bin get field_name;
 (prints "300" or "false" or "Welcome to the jungle")
 
 $ prog -i inputfile.bin set some_field:9000
 $ prog -i inputfile.bin set other_field:Whatever_String
 
 Each field itself is strongly typed, for what that's worth.
So you've got a large struct like this (right?): ---- struct S { int some_field; string other_field; /* ... more fields with arbitrary types ... */ } ----
 Approaches I have considered and implemented in part are:
      * templated getter (T get(T)(string field) {...}) but this approach 
 requires knowledge of field types which I cannot reasonably expect to 
 know at runtime(?)
The return type needs to be known at compile time, but `field` is passed at run time. Can't work.
      *  modification to the above whereby I could have an AA holding 
 type information for each field, generated by static foreach {mixin 
 ...}, although I cannot get this to work as my struct's static 
 constructor complains (rightly) that it cannot work without knowing 
 'this' at compile time. Code: `mixin("field_types[\"" ~ prop ~ "\"] = 
 typeid(this." ~ prop ~ ");");`  Is there another __trait I am missing 
 that will give me the type of the struct member without requiring an 
 instance of the struct?
You could use `typeid(typeof(this." ~ prop ~ "))`. But you can't use a run-time TypeInfo as a return type. So I don't think this gets you anywhere.
 I did manage to use metaprogramming inside my templated get function to 
 handle numeric values, which was fascinating (although this is probably 
 ugly code and it required a large enum array FIELDS):
 
 ```
          GetterSwitch:
          switch (field)
          {
              static foreach(prop; FIELDS ) {
                  mixin("case \"" ~ prop ~ "\": val = this." ~
prop ~ "; 
 break GetterSwitch;");
              }
              default:
                  val = 0;
                  assert(0);  // This is to prevent subtle
bugs, but I 
 need a better error handler
          }
 ```
You can probably get around the (manually maintained?) `FIELDS` array with `.tupleof` or something similar: ---- static foreach (i, f; S.tupleof) { case __traits(identifier, f): } ----
 Any pointers / design patterns on this particular type of problem class 
 would be greatly appreciated.  (Sidenote, I realize I could probably use 
 the witchcraft library, but I am also using this as exercise to learn D 
 beyond the basics).
You simply cannot have a method that returns different types based on a run-time value. You could possibly return a std.variant.Variant. But if the goal is just to print the value to the screen, all you need is a string. So the signature would be `string get(string field)`. And for the implementation you could use `.tupleof` to iterate over all fields, and then return `f.to!string`. `set` can be done similarly. Take two `string`s: the field name, and the value. `static foreach` over all fields. On a match, convert the given value string to the type of the field that matched.
Mar 14 2018
parent reply James Blachly <james.blachly gmail.com> writes:
On Wednesday, 14 March 2018 at 22:58:25 UTC, ag0aep6g wrote:
 You can probably get around the (manually maintained?) `FIELDS` 
 array with `.tupleof` or something similar:

 ----
 static foreach (i, f; S.tupleof)
 {
     case __traits(identifier, f):
 }
 ----

 Any pointers / design patterns on this particular type of 
 problem class would be greatly appreciated.  (Sidenote, I 
 realize I could probably use the witchcraft library, but I am 
 also using this as exercise to learn D beyond the basics).
You simply cannot have a method that returns different types based on a run-time value. You could possibly return a std.variant.Variant. But if the goal is just to print the value to the screen, all you need is a string. So the signature would be `string get(string field)`. And for the implementation you could use `.tupleof` to iterate over all fields, and then return `f.to!string`. `set` can be done similarly. Take two `string`s: the field name, and the value. `static foreach` over all fields. On a match, convert the given value string to the type of the field that matched.
Thanks - to!string certainly seems to be a good option in this case (CLI) and I was definitely overthinking this part, perhaps because I was trying to write everything as generically / extensibly as possible (for example, to use the same framework but with a GUI or web front end, for example). I would still think an AA mapping (string) field name to a type would be useful and will see if I can construct it as a mixin using typeof(Struct.member) somehow.
Mar 15 2018
parent Cym13 <cpicard openmailbox.org> writes:
On Thursday, 15 March 2018 at 15:48:52 UTC, James Blachly wrote:
 On Wednesday, 14 March 2018 at 22:58:25 UTC, ag0aep6g wrote:
 You can probably get around the (manually maintained?) 
 `FIELDS` array with `.tupleof` or something similar:

 ----
 static foreach (i, f; S.tupleof)
 {
     case __traits(identifier, f):
 }
 ----

 Any pointers / design patterns on this particular type of 
 problem class would be greatly appreciated.  (Sidenote, I 
 realize I could probably use the witchcraft library, but I am 
 also using this as exercise to learn D beyond the basics).
You simply cannot have a method that returns different types based on a run-time value. You could possibly return a std.variant.Variant. But if the goal is just to print the value to the screen, all you need is a string. So the signature would be `string get(string field)`. And for the implementation you could use `.tupleof` to iterate over all fields, and then return `f.to!string`. `set` can be done similarly. Take two `string`s: the field name, and the value. `static foreach` over all fields. On a match, convert the given value string to the type of the field that matched.
Thanks - to!string certainly seems to be a good option in this case (CLI) and I was definitely overthinking this part, perhaps because I was trying to write everything as generically / extensibly as possible (for example, to use the same framework but with a GUI or web front end, for example). I would still think an AA mapping (string) field name to a type would be useful and will see if I can construct it as a mixin using typeof(Struct.member) somehow.
If you're comming from python you may appreciate that you don't need getter/setters in D either. Just as you have property in python which allows you to change at any time from a simple attribute to a method (be it reading or writing) you have a property syntax in D: struct S { int a; int _b; auto b() { return _b; } void b(int val) { _b = val; } } void main(string[] args) { S s; s.a = 24; writeln(s.a); s.b = 42; writeln(s.b); }
Mar 15 2018