On Sunday, 15 March 2020 at 20:18:03 UTC, James Blachly wrote:
I would like to programmatically retrieve members of a subclass
to create a self-documenting interface. I am afraid that my
approach is not possible due to need for compile time __traits
/ std.traits, and runtime typeinfo. My proposed approach is as
follows:
class Base
{
string whatever;
string toString()
{
// loop over __traits(allMembers, typeof(this)) or
getSymbolsByUDA, etc.
}
}
/// There may be a dozen Derived classes
class Derived1 : Base
{
@Config("a name", "a description")
float probability;
}
class Derived2 : Base
{
@Config("another", "more description")
int replicates;
}
...
There's no built-in way to do this - toString() doesn't know what
derived classes exist, and more could be added from other modules
and even dynamically loaded libraries. Now, there are ways to do
something somewhat like it. I would suggest creating an abstract
method in the base class, that derived class would have to
override, and use a template this argument to get the correct
type for __traits(allMembers) to operate on:
class Base {
override string toString() {
return toStringImpl();
}
abstract string toStringImpl();
string toStringBase(this That)() {
// foreach (e; __traits(allMembers, That) {...}
}
}
class Derived : Base {
override string toStringImpl() {
return this.toStringBase();
}
}
Since toStringImpl will always call toStringBase, this could
perhaps better be modeled with a template mixin:
mixin template DerivedToString() {
override string toStringImpl() {
return this.toStringBase();
}
}
class Derived2 : Base {
mixin DerivedToString!();
}
This way, you can have all the logic of toString in the base
class, and the only thing a subclass will have to include is one
line for the mixin. In addition, since toStringImpl is abstract,
the implementer of a subclass will get a compile-time error if he
or she forgets to do either the mixin or override toStringImpl
themselves.
--
Simen