Bug 12732 – Add an Appender-like template that recursively builds a structure of Appender fields

Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-05-11T14:33:31Z
Last change time
2022-07-06T05:17:59Z
Assigned to
No Owner
Creator
Andrej Mitrovic

Comments

Comment #0 by andrej.mitrovich — 2014-05-11T14:33:31Z
This seems to be a common occurrence in my code: ----- import std.array; alias vec3 = int[3]; /// struct Model { vec3[] indices; vec3[] vertices; vec3[] normals; } Model loadModel(string path) { // note the code duplication here, needs to be tracked // separately to the Model struct. Appender!(vec3[]) indices; Appender!(vec3[]) vertices; Appender!(vec3[]) normals; // ... // indices ~= // vertices ~= return Model(indices.data, vertices.data, normals.data); } void main() { } ----- To avoid this code duplication it would be great to have a helper template that can return an equivalent structure which contains Appender fields for all internal arrays, and a convenience .data property function that returns the original type. Here's one implementation: ----- import std.array; import std.typetuple; alias vec3 = int[3]; alias ApplyAppender(T : E[], E) = Appender!T; alias ApplyAppender(T) = T; struct AppenderWrapper(T) { alias Fields = staticMap!(ApplyAppender, typeof(T.tupleof)); Fields fields; alias fields this; @property T data() { T res; foreach (idx, field; fields) { static if (is(typeof(res.tupleof[idx]) : E[], E)) res.tupleof[idx] = field.data; else res.tupleof[idx] = field; } return res; } } /// struct Model { vec3[] indices; vec3[] vertices; vec3[] normals; } Model loadModel(string path) { AppenderWrapper!(typeof(return)) result; // ... // result.indices ~= // result.vertices ~= return result.data; } void main() { } -----
Comment #1 by andrej.mitrovich — 2014-05-11T14:39:21Z
Oops, I failed to properly test that implementation. Lemme fix that up real soon.
Comment #2 by andrej.mitrovich — 2014-05-11T14:45:44Z
Here's a working version, unfortunately I again had to resort to string mixins: ----- import std.array; import std.string; import std.typetuple; alias vec3 = int[3]; template ApplyAppender(alias S) { static if (is(typeof(S) : E[], E)) enum ApplyAppender = format("Appender!(%s) %s;", typeof(S).stringof, __traits(identifier, S)); else enum ApplyAppender = format("%s %s;", typeof(S).stringof, __traits(identifier, S)); } string generate(T)() { string[] res; foreach (str; staticMap!(ApplyAppender, T.tupleof)) res ~= str; return res.join("\n"); } struct AppenderWrapper(T) { mixin(generate!T); @property T data() { T res; foreach (idx, field; this.tupleof) { static if (is(typeof(res.tupleof[idx]) : E[], E)) res.tupleof[idx] = field.data; else res.tupleof[idx] = field; } return res; } } /// struct Model { vec3[] indices; vec3[] vertices; vec3[] normals; int other; } Model loadModel(string path) { AppenderWrapper!(typeof(return)) result; vec3 vec; result.indices ~= vec; result.vertices ~= vec; result.normals ~= vec; result.other = 5; return result.data; } void main() { } -----
Comment #3 by andrej.mitrovich — 2014-05-11T14:47:33Z
And because of string mixins the mixin() won't work if the type is from another module which isn't imported. Well maybe we can implement this without string mixins somehow.
Comment #4 by andrej.mitrovich — 2014-05-11T14:52:18Z
There we go: ----- import std.array; import std.string; import std.typetuple; alias ApplyAppender(T : E[], E) = Appender!T; alias ApplyAppender(T) = T; enum Identifier(alias S) = __traits(identifier, S); string generateAliases(T)() { string[] res; foreach (idx, str; staticMap!(Identifier, T.tupleof)) res ~= format("alias %s = fields[%s];", str, idx); return res.join("\n"); } struct AppenderWrapper(T) { alias Fields = staticMap!(ApplyAppender, typeof(T.tupleof)); Fields fields; mixin(generateAliases!T); @property T data() { T res; foreach (idx, field; fields) { static if (is(typeof(res.tupleof[idx]) : E[], E)) res.tupleof[idx] = field.data; else res.tupleof[idx] = field; } return res; } } alias vec3 = int[3]; struct Model { vec3[] indices; vec3[] vertices; vec3[] normals; int other; } Model loadModel(string path) { AppenderWrapper!(typeof(return)) result; vec3 vec; result.indices ~= vec; result.vertices ~= vec; result.normals ~= vec; result.other = 1; return result.data; } void main() { } -----
Comment #5 by andrej.mitrovich — 2022-07-06T05:17:59Z
This doesn't need to be part of Phobos, it's a very specific use-case that can easily be implemented in a user library (or even just a few lines of code these days).