What is the "right" way to create a generic type getter (and setter) ?
ag0aep6g
anonymous at example.com
Wed Mar 14 22:58:25 UTC 2018
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.
More information about the Digitalmars-d-learn
mailing list