Problem: Cannot create class out of nothing using witchcraft

Benjamin Thaut code at benjamin-thaut.de
Tue Oct 15 06:01:42 PDT 2013


Am 15.10.2013 10:03, schrieb DoctorCaptain:
> dmd v2.063.2
>
> Hi there! I'm terribly hopeful that you're more interested in the
> problem at hand than my choice of title.
>
> I've been using D for a while as my language of choice for
> various projects here and there, and I've recently discovered
> that template programming is magic. This is good. As part of the
> process of finding anything magic that also works, I've been
> going completely overboard with it. This involves, among other
> things, creating empty classes using templates and mixins at
> compile time, to build an inheritance hierarchy to do fancy
> things. This is all essentially irrelevant, but brings us to my
> issue.
>
> The next logical step in my adventure here was to see if I could
> create classes that weren't empty, but in fact has data members,
> generated at compile time using variadic templates. The first
> run-through went super well, and I was able to create classes
> with arbitrary primitive data types.
>
> However, ultimately I wanted these compile-time-generated classes
> with arbitrary data members to have data members that were of the
> type of the other classes that were also generated at compile
> time.
>
> That is to say, I want to generate, at compile time, classes like
> this:
>
> class MyClass {
>       crazyTemplate!("MagicClassOne").MagicClassOne t1;
>       crazyTemplate2!("MagicClassTwo").MagicClassTwo t2;
> }
>
> such that crazyTemplate and crazyTemplate2 each generate a class
> definition at compile time, accessible as defined by the string
> parameter.
>
> For example:
>
> template crazyTemplate(string classname) {
>       mixin(`class ` ~ className ~ ` {}`);
> }
>
> The bare class-generating templates work, and generating classes
> that have arbitrary members at compile time using variadic
> templates given an arbitrary list of basic types works, but as
> soon as I try to use these template-generated classes as the
> types of the members of this variadic template, things go awry.
>
> I feel like what I'm trying to explain is a bit difficult to
> parse without a minimal working example, so here is one:
>
> code
> -------------
> import std.stdio;
> import std.typetuple;
> import std.traits;
> import std.conv;
>
> class BaseClass {}
>
> template ChildT(string className)
> {
>       mixin(`class ` ~ className ~ ` : BaseClass {}`);
> }
>
> template isChildOrBaseClass(T)
> {
>       enum bool isChildOrBaseClass = is(T : BaseClass);
> }
>
> template GrabBagT(string className, T...)
>       if (allSatisfy!(isChildOrBaseClass, T))
> {
>       mixin(genClassStr!(T)(className));
> }
>
> string genClassStr(T...)(string className)
> {
>       string classStr = "";
>       classStr ~= `static class ` ~ className ~ ` : BaseClass`;
>       classStr ~= `{`;
>       // Demonstrate that the template itself is not the problem
>       classStr ~= `    ChildT!("BestChild").BestChild t1000;`;
>       // Add arbitrary data members
>       foreach (i, TI; T)
>       {
>           // Neither of these work with the generated classes, but
> the first
>           // will work if GrabBagT is called with primitive types
> and its
>           // constraint is commented out
>           //classStr ~= fullyQualifiedName!(TI) ~ ` t` ~
> to!(string)(i) ~ `;`;
>           //classStr ~= __traits(identifier, TI) ~ ` t` ~
> to!(string)(i) ~ `;`;
>       }
>       classStr ~= `    void printAttempts() {`;
>       foreach (i, TI; T)
>       {
>           classStr ~= `    writeln(` ~ to!string(i) ~ `);`;
>       }
>       classStr ~= `    }`;
>       classStr ~= `}`;
>       return classStr;
> }
>
> int main(string[] args)
> {
>       alias ChildT!("WorstChild").WorstChild WorstChild;
>       alias ChildT!("MiddleChild").MiddleChild MiddleChild;
>
>       auto magicObject = new GrabBagT!(
>           "MagicClass", WorstChild, MiddleChild).MagicClass();
>
>       //auto magicObject = new GrabBagT!(
>       //    "MagicClass", int, float).MagicClass();
>
>       magicObject.printAttempts();
>
>       return 0;
> }
> -------------
>
> That should compile and print out "0" and "1". Note the template
> constraint that bars the template instantiator from trying to
> instantiate GrabBagT with anything other than types that can be
> implicitly treated as type BaseClass. I tried using
> __traits(identifier) and fullyQualifiedName to get the actual
> type of the alias passed in (like the BestChild declaration), but
> the latter gives a mangled name, and the former gives just
> whatever the bare alias was.
>
> In any case. If the fullyQualifiedName line is uncommented, the
> error is:
>
> ------------
> classAttributeGen.d(22): Error: undefined identifier
> '__T6ChildTVAyaa10_576f7273744368696c64Z'
> classAttributeGen.d(22): Error:
> classAttributeGen.__T6ChildTVAyaa10_576f7273744368696c64Z.WorstChild
> is used as a type
> classAttributeGen.d(22): Error: undefined identifier
> '__T6ChildTVAyaa11_4d6964646c654368696c64Z'
> classAttributeGen.d(22): Error:
> classAttributeGen.__T6ChildTVAyaa11_4d6964646c654368696c64Z.MiddleChild
> is used as a type
> classAttributeGen.d(54): Error: template instance
> classAttributeGen.GrabBagT!("MagicClass", WorstChild,
> MiddleChild) error instantiating
> ------------
>
> If the __traits line is uncommented instead, the error is:
>
> ------------
> classAttributeGen.d(22): Error: undefined identifier WorstChild
> classAttributeGen.d(22): Error: undefined identifier MiddleChild
> classAttributeGen.d(54): Error: template instance
> classAttributeGen.GrabBagT!("MagicClass", WorstChild,
> MiddleChild) error instantiating
> ------------
>
> Of particular note is that we are giving the class a
> ChildT!("BestChild").BestChild member, which shows that simply
> using the empty-class-generating-templates is not the problem,
> but rather the way the type tuple is fed in to the variadic
> template.
>
> If the GrabBagT constraint is commented out, the uncommented
> magicObject instantiation is commented, the commented magicObject
> instantiation is uncommented, and the fullyQualifiedName line is
> uncommented, then this will also compile, demonstrating that a
> tuple of primitive data types can be used to generate classes at
> compile time with arbitrary, primitive data members.
>
> Ultimately, the problem I am trying to solve is generating class
> definitions at compile time with arbitrary, templated data
> members. If this is the wrong way to do it, by all means point me
> in the right direction. However, as an exercise to the community,
> is it even possible to do it the way I'm attempting here? Is it
> possible to do at all? Is there something awful about how
> templates are passed around that makes it fundamentally
> impossible to get their underlying type information, like with
> the working BestClass member, if they're passed in as template
> type parameters?
>
> If what I am asking is unclear, I will be more than happy to
> explain in a different way. I tried to be simultaneously as
> succinct and as comprehensive as possible with what the issue is.
> I also tried to find the answer to this problem, as I have with
> every other problem I've faced with D, through an exhaustive
> search of the various resources available to us, but alas I could
> find nothing. Of course, this is a fairly esoteric scenario, so
> that's entirely acceptable. That said, ideally the definitive
> answer can be found in future searches by finding this post.
>
> With a quiet but definitive stepping-aside to the inquiry of
> "What why are you doing this."

If I move the alias declarations of WorstChild and MiddleChild outside 
of main end then uncomment the __traits(identifier, ...) line it works.

-- 
Kind Regards
Benjamin Thaut


More information about the Digitalmars-d-learn mailing list