Thread overview | ||||||
---|---|---|---|---|---|---|
|
March 14, 2018 What is the "right" way to create a generic type getter (and setter) ? | ||||
---|---|---|---|---|
| ||||
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 |
March 14, 2018 Re: What is the "right" way to create a generic type getter (and setter) ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to James Blachly | 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. |
March 15, 2018 Re: What is the "right" way to create a generic type getter (and setter) ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to ag0aep6g | 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.
|
March 15, 2018 Re: What is the "right" way to create a generic type getter (and setter) ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to James Blachly | 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);
}
|
Copyright © 1999-2021 by the D Language Foundation