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.

Reply via email to