templated toString and class inheritance
cc
cc at nevernet.com
Sun Dec 25 19:12:54 UTC 2022
On Sunday, 25 December 2022 at 19:10:13 UTC, cc wrote:
> I have come up with one solution, which I will attach in the
> next post.
Here is what I am playing with currently. I don't know if it's
the best way to handle the situation. Redundant checks end up
being made. Given that there are two levels of indirection that
need to be considered (What OutputRange template is being passed
around? and What is the runtime type of the object being
written?), I had to get a little creative, I think. Since we
can't cleanly anticipate which matches of
toString(W)..isOutputRange will be used (and could be a voldemort
type) I have incorporated the registering of classes directly
into the toString handler, but this can still potentially fail if
derived classes are spread into different files and not called
first.
```d
module util.tostringmaster;
import std.string;
import std.traits;
import std.format;
import std.range.primitives;
// TODO: Make sure toString template is actually compatible with
the desired W
// Proper error handling/fallback if matching toString
doesn't exist
abstract final class ToStringMaster {
private:
static abstract class ToStringBase {}
static abstract class ToStringWriter(W) : ToStringBase {
void writeWith(ref W writer, Object obj);
}
static final class ToStringHolder(W, T) : ToStringWriter!W {
override void writeWith(ref W writer, Object obj) {
T t = cast(T) obj;
t.toString(writer);
}
}
static ToStringBase[string][string] table;
static void[0][string] writerTable;
static void registerAll(W, string MOD)() {
enum WNAME = fullyQualifiedName!W;
enum MNAME = WNAME~":"~MOD;
//pragma(msg, "REGISTERALL "~MNAME);
if (MNAME in writerTable)
return;
writerTable.require(MNAME);
{
mixin(`import `~MOD~`;`);
mixin(`alias ALL = __traits(allMembers, `~MOD~`);`);
static foreach (sym; ALL) {{
mixin(`alias SYM = __traits(getMember, `~MOD~`, "`~sym~`");`);
static if (is(SYM == class)) {
alias CLASS = SYM;
alias TOSTRING = __traits(getMember, CLASS, "toString");
static if (__traits(isTemplate, TOSTRING)) {
register!(W, CLASS);
}
}
}}
// Explicit class registration
//register!(W, Animal);
//register!(W, Dog);
//register!(W, Bulldog);
}
}
static void register(W, T)() {
enum WNAME = fullyQualifiedName!W;
enum TNAME = fullyQualifiedName!T;
table.require(WNAME);
table[WNAME][TNAME] = new ToStringHolder!(W, T);
}
static void redirect(W, T)(ref W writer, T obj) {
enum WNAME = fullyQualifiedName!W;
static if (!(WNAME.indexOf("__lambda") >= 0)) { // Don't
register hasToString, etc
registerAll!(W, moduleName!(T));
scope auto tname = typeid(obj).name;
if (auto wp = WNAME in table) {
if (auto p = tname in *wp) {
auto tsh = cast(ToStringWriter!W) *p;
assert(tsh, "Invalid ToStringWriter: "~WNAME);
tsh.writeWith(writer, obj);
return;
}
}
writer.formattedWrite("<Unknown:%s>", tname);
}
}
}
// Mixin alternative instead of delegates
/* enum string ToStringReal = q{
static if (!(fullyQualifiedName!W.indexOf("__lambda") >= 0)) {
// hasToString
static assert(__FUNCTION__.endsWith(".toString"), "Only include
this mixin in toString");
if (typeid(this) !is typeof(this).classinfo) {
ToStringMaster.redirect(writer, this);
return;
}
}
}; */
void realToStringOr(T,W)(T obj, ref W writer, scope void
delegate() origDG = null) if (isOutputRange!(W, char)) {
if (typeid(obj) !is T.classinfo) {
ToStringMaster.redirect(writer, obj);
return;
}
if (origDG !is null) origDG();
}
```
```d
class Animal {
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
realToStringOr(this, writer, {
writer.formattedWrite("Animal()");
});
}
}
class Dog : Animal {
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
realToStringOr(this, writer, {
writer.formattedWrite("Dog()");
});
}
}
final class Bulldog : Dog {
bool sunglasses;
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
// We don't need realToStringOr if we know for certain this
class will never be derived, but...
writer.formattedWrite("Bulldog(cool: %s)", sunglasses);
}
}
void main() {
auto animals = [new Animal, new Dog, new Bulldog];
foreach (animal; animals) {
animal.writeln;
}
}
```
```
Animal()
Dog()
Bulldog(cool: false)
```
More information about the Digitalmars-d
mailing list