www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Problem: Cannot create class out of nothing using witchcraft

reply "DoctorCaptain" <garble harble.com> writes:
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."
Oct 15 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
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
Oct 15 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Your going way to complicated by actually passing the variadic arguments 
to the generator function. If you don't pass the variadic arguments to 
the generator function its way simpler and also works ;-)

http://dpaste.dzfl.pl/59e2547b
Oct 15 2013
parent reply "DoctorCaptain" <garble harble.com> writes:
On Tuesday, 15 October 2013 at 13:14:46 UTC, Benjamin Thaut wrote:
 Your going way to complicated by actually passing the variadic 
 arguments to the generator function. If you don't pass the 
 variadic arguments to the generator function its way simpler 
 and also works ;-)

 http://dpaste.dzfl.pl/59e2547b
Thank you for your responses! The following is a modified version of my original post code that incorporates a bit from all the answers I received, and certainly puts me farther along than I was: http://dpaste.dzfl.pl/f7508d25 Taking Benjamin Thaut's suggestion to move the alias's into the global scope, GrabBagT now has access to the types it needs to know about, and my witchcraft abomination of a magic class works as long as everything is available in the same file. However. My original goal, going back to how I want to use this in the project I'm working on, is to define GrabBagT, and the associated templates, in a different file, such that I need only import the file and call: auto magicObject = new GrabBagT!("MagicClass", SomeTypeAlias, SomeOtherTypeAlias).MagicClass(); Which brings us back to the original problem, that passing in aliases to instantiations of templates doesn't work, because the type information is lost. I understand the fundamental issue you folks were bringing up in your posts: GrabBagT cannot possibly actually have access to the real types of the aliases it's receiving, as those types aren't available in its scope (which is why bringing the aliases originally defined within main() out into the global scope makes things work). But then this raises another fundamental issue, as I'm doing this exact thing successfully (passing aliases of template instantiations to other templates), where the only difference is the receiving templates do not have variadic parameters, and I throw the actual template parameter into the mixin itself. That is to say: If the template I am trying to instantiate IS NOT variadic, and I pass in an alias of an instantiated template, then the receiving template has all of the type information it needs. Example: dpaste.dzfl.pl/6d618af9 If the template I am trying to instantiate IS variadic, and I pass in a variadic list of aliases of instantiated templates, then the receiving template appears unable to retrieve all of the type information it needs out of each of the indexes of the variadic parameter. We get close with this next example, but the problem appears to be that it tries to create a static array of the tuple T of length i, instead of indexing into it to get the type (but if you provide an i greater than the actual length of the tuple, it complains about going out of bounds of the tuple length, meaning that it IS in fact aware that it's supposed to be indexing into a type tuple): dpaste.dzfl.pl/ff8a5b9d Given that the compilation error messages in the last DPaste refer to the mixed in code after the class definition string is generated and returned, the issue has to be in the way the type tuple is treated, itself. So, first, the template argument (variadic or otherwise) needs to be part of the STRING that is constructed and passed back to the mixin, so that it can then just naturally access GrabBagT's T type tuple parameter. This is how the non-variadic DPaste example works. Second, the variadic version seems to fail because the type tuple is not indexed correctly, to retrieve the type at that index, because it instead appears to try to make a static array of the whole tuple (which is odd because if you try to index the tuple with an index beyond its length, it suddenly knows it's a type tuple again). And to be absolutely clear, the underlying goal is to generate a class definition at runtime, which I can mix in and then instantiate, that can contain an arbitrary list of data members that are themselves generated at compile time by their own templates. I have demonstrated that it is possible to do this with a single arbitrary template instantiation in dpaste.dzfl.pl/6d618af9 , but as soon as we try to do it with type tuples, it chokes. I am not sure if this is a compiler bug or I'm just out of my mind, but again, for the sake of academic advancement, lets try to solve the problem in complete disregard of whether we should. If any part of my explanation of our current status on this problem is unclear, I would be happy to explain in an alternate way. Thank you again!
Oct 15 2013
parent reply "DoctorCaptain" <garble harble.com> writes:
Fixing links:

 If the template I am trying to instantiate IS NOT variadic, and 
 I pass in an alias of an instantiated template, then the 
 receiving template has all of the type information it needs. 
 Example: dpaste.dzfl.pl/6d618af9
http://dpaste.dzfl.pl/6d618af9
 If the template I am trying to instantiate IS variadic, and I 
 pass in a variadic list of aliases of instantiated templates, 
 then the receiving template appears unable to retrieve all of 
 the type information it needs out of each of the indexes of the 
 variadic parameter. We get close with this next example, but 
 the problem appears to be that it tries to create a static 
 array of the tuple T of length i, instead of indexing into it 
 to get the type (but if you provide an i greater than the 
 actual length of the tuple, it complains about going out of 
 bounds of the tuple length, meaning that it IS in fact aware 
 that it's supposed to be indexing into a type tuple): 
 dpaste.dzfl.pl/ff8a5b9d
http://dpaste.dzfl.pl/ff8a5b9d
 And to be absolutely clear, the underlying goal is to generate 
 a class definition at runtime, which I can mix in and then 
 instantiate, that can contain an arbitrary list of data members 
 that are themselves generated at compile time by their own 
 templates. I have demonstrated that it is possible to do this 
 with a single arbitrary template instantiation in 
 dpaste.dzfl.pl/6d618af9 , but as soon as we try to do it with 
 type tuples, it chokes. I am not sure if this is a compiler bug 
 or I'm just out of my mind, but again, for the sake of academic 
 advancement, lets try to solve the problem in complete 
 disregard of whether we should.
http://dpaste.dzfl.pl/6d618af9
Oct 15 2013
parent reply "DoctorCaptain" <garble harble.com> writes:
I missed an extremely critical point. The working example with 
the single arbitrary template instantiation as a data member of 
the generated class, http://dpaste.dzfl.pl/6d618af9 , has the 
template instantiation aliases inside of main, meaning the alias 
is NOT within the scope of GrabBagT, but it is able to get all of 
the type information it needs from the alias being passed in. 
Which reaffirms to me that the problem is not about whether 
GrabBagT needs to be able to see what the alias refers to 
(clearly it can figure it out), but that once these aliases are 
wrapped up into a tuple passed into the variadic version of the 
template, it stops being able to figure it out. Either the syntax 
I'm using is wrong, there is a bug in the compiler, or for some 
reason this extremely specific scenario isn't allowed.

In any case, example code that demonstrates the principle we are 
trying to achieve as working should be able to call 
magicObject.printSelf() and show that each of its arbitrary data 
members can call their own printSelf() methods successfully, as 
with the http://dpaste.dzfl.pl/6d618af9 example, but with an 
arbitrary list of instantiated templates, instead of just the one.

Brain buster, this one.
Oct 15 2013
parent reply "DoctorCaptain" <garble harble.com> writes:
I've gotten extremely close. The DPaste link that follows 
demonstrates three different templates:

The first template is capable of generating the string for and 
mixing in the definition of a class that has a single arbitrary 
argument. An object of that class is instantiated, and its 
printSelf() method is invoked to demonstrate that the arbitrary 
data member it was generated with is indeed what you would expect 
it to be. The magic here is fairly plain.

The second template is capable of generating the string for the 
definition of a class that has some arbitrary number of arbitrary 
arguments at -compile- time, through the use of variadic template 
parameters. The issue with this string is that the type at T[n], 
where T is the variadic template parameter, is mangled at 
compile-time. The data-member-generating foreach loop shows the 
three potential ways I attempted to get at the full type 
information. All three have some problem with it, with typeid 
giving a fully mangled type, fullyQualifiedName giving a mostly 
mangled type, and __traits(identifier) giving just whatever the 
alias was that was passed in. The two other commented-out code 
sections in this template was an attempt to resolve the type 
information at the time of mixin, rather than within the string 
generation function, but this doesn't work.

The third template demonstrates that D actually can resolve the 
individual types of the variadic template parameter correctly, so 
long as it's allowed to do this at runtime. The string printed as 
a result of instantiating this template shows exactly the class 
definition we want to be able to mix in at compile time, but this 
type information eludes us at compile time even though it's 
clearly readily available at runtime.

The code referenced above is here: http://dpaste.dzfl.pl/a6feafc8

I'm not sure why DPaste is failing to compile the code, but I am 
compiling it with DMD v2.063.2. Also included at the bottom of 
the DPaste listing is the output I get when running that program, 
exactly as listed.

With all of that said, there is a fundamental difference between 
how the first template and the second two templates are written. 
The first one, which works, resolves the T parameter during the 
mixin. The second two try to resolve the variadic types during 
class definition string generation. I am pretty convinced at this 
point that if this is ever going to work, the variadic template 
parameter type tuple needs to be resolved during the mixin, like 
with the first example. The how is the question. Again, I 
attempted to have the variadic template parameter types deduced 
during execution of the mixin with the two blocks of 
commented-out code in the second template, but I had no luck 
there, as I got the same errors as I did in my previous big post. 
So.

By the first template, we know that we can generate class 
definitions with arbitrary data members and mix them in at 
compile time using aliases for templated, 
compile-time-mixin-generated classes, but for each new arbitrary 
type we want the generated class to have, we need to have a 
distinct template parameter for the type.

By the third template, we know that we should have access to the 
fully qualified types in the variadic template parameter, but it 
at least appears to only be fully resolved at runtime (which I 
feel like is me misunderstanding something, but then that's also 
the root of our problem).

By the second template, we show that trying to deduce the types 
within the type tuple (the variadic template parameter) during 
class definition string generation does not work, which leads us 
to believe it must be resolved while mixing in the class 
definition using the templated types, but that also doesn't seem 
to work (as noted by the failed attempts in the two commented out 
blocks of the second template).

I feel like we're so very close to the answer, whether it's "Oh, 
you just have to do this" or "You're crazy, this is impossible."

If only it was possible to summon Andrei. He'd know! Maybe it's 
some sort of a Bloody Mary thing?

Andrei, Andrei, An-
Oct 15 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 16.10.2013 03:17, schrieb DoctorCaptain:
 I've gotten extremely close. The DPaste link that follows demonstrates
 three different templates:

 ...
So is there any reason why you still pass the variadic arguments to the generator function and not do it the way I proposed in my last dpaste snippet? Even if you need the variadic arguments inside the generator for some logic you could still reference them by using the variadic arguments to the actual template.
Oct 15 2013
parent reply "DoctorCaptain" <garble harble.com> writes:
On Wednesday, 16 October 2013 at 06:09:48 UTC, Benjamin Thaut 
wrote:
 Am 16.10.2013 03:17, schrieb DoctorCaptain:
 I've gotten extremely close. The DPaste link that follows 
 demonstrates
 three different templates:

 ...
So is there any reason why you still pass the variadic arguments to the generator function and not do it the way I proposed in my last dpaste snippet? Even if you need the variadic arguments inside the generator for some logic you could still reference them by using the variadic arguments to the actual template.
I suppose I was trying to give the class individual data members, such that passing a tuple of (WorstChild, MiddleChild, BestChild) would result in a class definition of: class MagicClass { WorstClass t1; MiddleClass t2; BestClass t3; } However, if that is impossible (and frankly it'd be more difficult to work with than your way even if it was achieved), then I'll use your approach (actually, I'm just going to use your approach anyway. It's significantly cleaner than my horrid mess of a goal). That said, what is actually happening in your example? I reworked it a bit to demonstrate that the class has accessible members within T, that can be instantiated and accessed and whatnot. http://dpaste.dzfl.pl/07b20d75 Note the use of typeof() to get the type of the elements at each index of members, to generate a type on which a constructor can be called to instantiate the elements at each index of members. Magic. What is T within the generated class definition? Is this just how tuples work? T is an "array" of three arbitrary pointers, initially null, that can be instantiated with new to create valid pointers to objects of each type at T's indexes? Like, T in the DPaste example is an array of pointers, such that: [WorstChild*, MiddleChild*, BestChild*]? Is it literally just magic like that? Actually, let me formally present a couple of ending thoughts/questions: First, allow me to apologize for trying to force a different, messy horrid mess of a goal on you, when your initial solution was majestic to begin with. Second, as I was trying to figure out just a moment ago, what -exactly- is T in the generated class definition, and why does it allow me to use it as an arbitrary container for objects of the arbitrary types I want it to contain (Read: why is it able to do exactly what I want it to be able to do? What's going on behind the scenes?)? Third, given that I am going to go with your solution (it works, it works well, and I can't seem to force my original goal to work, whether that was even a good idea to begin with at all (it wasn't)), can you think of a way to actually produce my original goal? That is, instead of a container T which I can use to access my arbitrary data members once the generated class is instantiated, is it actually even possible to generate individual data members, a la: class MagicClass { T[0] t0; T[1] t1; // ... etc } // ? You've been wildly helpful, and admirably concise. I am -extremely- interested in the answer to the second thought, and while I'm sure the answer is simple, it also seems -too- magic to actually work, and yet it does. Again, thank you to everyone who responded, and thank you Benjamin for your continued help.
Oct 16 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 16.10.2013 10:40, schrieb DoctorCaptain:
 On Wednesday, 16 October 2013 at 06:09:48 UTC, Benjamin Thaut wrote:
 Am 16.10.2013 03:17, schrieb DoctorCaptain:
 I've gotten extremely close. The DPaste link that follows demonstrates
 three different templates:

 ...
So is there any reason why you still pass the variadic arguments to the generator function and not do it the way I proposed in my last dpaste snippet? Even if you need the variadic arguments inside the generator for some logic you could still reference them by using the variadic arguments to the actual template.
I suppose I was trying to give the class individual data members, such that passing a tuple of (WorstChild, MiddleChild, BestChild) would result in a class definition of: class MagicClass { WorstClass t1; MiddleClass t2; BestClass t3; } However, if that is impossible (and frankly it'd be more difficult to work with than your way even if it was achieved), then I'll use your approach (actually, I'm just going to use your approach anyway. It's significantly cleaner than my horrid mess of a goal). That said, what is actually happening in your example? I reworked it a bit to demonstrate that the class has accessible members within T, that can be instantiated and accessed and whatnot. http://dpaste.dzfl.pl/07b20d75 Note the use of typeof() to get the type of the elements at each index of members, to generate a type on which a constructor can be called to instantiate the elements at each index of members. Magic. What is T within the generated class definition? Is this just how tuples work? T is an "array" of three arbitrary pointers, initially null, that can be instantiated with new to create valid pointers to objects of each type at T's indexes? Like, T in the DPaste example is an array of pointers, such that: [WorstChild*, MiddleChild*, BestChild*]? Is it literally just magic like that? Actually, let me formally present a couple of ending thoughts/questions: First, allow me to apologize for trying to force a different, messy horrid mess of a goal on you, when your initial solution was majestic to begin with.
No problem, I can understand that you want to stick with your solution. But as Artur explained the template is actually not able to see the actual types directly and thats why full qualified identifier will not work.
 Second, as I was trying to figure out just a moment ago, what -exactly-
 is T in the generated class definition, and why does it allow me to use
 it as an arbitrary container for objects of the arbitrary types I want
 it to contain (Read: why is it able to do exactly what I want it to be
 able to do? What's going on behind the scenes?)?
T is the variadic argument T that is passed to the actual template "GrabBagT". It might become more clear when actually copy & pasting the generated code into the template. The problem is that you try to reference the classes by name. Thats not necessary. You get the classes passed in as template arguments. So you just reference them as template arguments instead of referencing them by name. It is possible to do so by not generating access via the name but instead use the template arguments passed to GrabBagT. I hope this made it a bit more clear. I might have some more time later today to look into your new examples and modify them.
 Third, given that I am going to go with your solution (it works, it
 works well, and I can't seem to force my original goal to work, whether
 that was even a good idea to begin with at all (it wasn't)), can you
 think of a way to actually produce my original goal? That is, instead of
 a container T which I can use to access my arbitrary data members once
 the generated class is instantiated, is it actually even possible to
 generate individual data members, a la:

 class MagicClass {
    T[0] t0;
    T[1] t1;
    // ... etc
 } // ?
What do you need individual data members for? In my example the "T members" is actually a instance of a tuple which actually comes down to the exakt same data layout like individual data members. The only difference is that you access them by index and not by name. Which is actually a plus if you ask me for generic programming. You can even get back the tuple by doing typeof(members);
 You've been wildly helpful, and admirably concise. I am -extremely-
 interested in the answer to the second thought, and while I'm sure the
 answer is simple, it also seems -too- magic to actually work, and yet it
 does.

 Again, thank you to everyone who responded, and thank you Benjamin for
 your continued help.
No problem. I love the metaprogramming abilities of D and a occansional challenge is always welcome ;-) -- Kind Regards Benjamin Thaut
Oct 16 2013
parent Benjamin Thaut <code benjamin-thaut.de> writes:
Oh something I forgot. You should really start littering your code with
pragma(msg, ...) statements to better understand what it does.
You can for example make your generator output "pramga(msg, 
T.stringof);" to find out what T actually is.

Kind Regards
Benjamin Thaut
Oct 16 2013
prev sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 16.10.2013 10:40, schrieb DoctorCaptain:
 http://dpaste.dzfl.pl/07b20d75

 Note the use of typeof() to get the type of the elements at each index
 of members, to generate a type on which a constructor can be called to
 instantiate the elements at each index of members. Magic.
There is actually a easier way to instanciate the elements. Just do "new T[i]();" no need for typeof. -- Kind Regards Benjamin Thaut
Oct 16 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 16.10.2013 16:08, schrieb Benjamin Thaut:
 Am 16.10.2013 10:40, schrieb DoctorCaptain:
 http://dpaste.dzfl.pl/07b20d75

 Note the use of typeof() to get the type of the elements at each index
 of members, to generate a type on which a constructor can be called to
 instantiate the elements at each index of members. Magic.
There is actually a easier way to instanciate the elements. Just do "new T[i]();" no need for typeof.
I'm actually wrong. "new T[i]()" will not work because the compiler will think it is a array allocation. You actually have to use new typeof(member[i])(); I created a example which generates individual class members for each of the arguments bassed to GrabBagT: http://dpaste.dzfl.pl/eef2edec -- Kind Regards Benjamin Thaut
Oct 16 2013
parent reply "DoctorCaptain" <garble harble.com> writes:
On Wednesday, 16 October 2013 at 18:47:25 UTC, Benjamin Thaut 
wrote:
 Am 16.10.2013 16:08, schrieb Benjamin Thaut:
 Am 16.10.2013 10:40, schrieb DoctorCaptain:
 http://dpaste.dzfl.pl/07b20d75

 Note the use of typeof() to get the type of the elements at 
 each index
 of members, to generate a type on which a constructor can be 
 called to
 instantiate the elements at each index of members. Magic.
There is actually a easier way to instanciate the elements. Just do "new T[i]();" no need for typeof.
I'm actually wrong. "new T[i]()" will not work because the compiler will think it is a array allocation. You actually have to use new typeof(member[i])(); I created a example which generates individual class members for each of the arguments bassed to GrabBagT: http://dpaste.dzfl.pl/eef2edec
AWW you posted that while I was writing my latest novel. So T[i] doesn't work? I guess I shouldn't have opened my eyes this morning. In any case, typeof() DOES work, so as long as there is a way to extract the type, we're good. I am extremely pleased it's actually possible to get individual data members like I was originally attempting to do. I'm likely not going to actually do this, as your T members; solution is cleaner, but I'm glad it's not just arbitrarily impossible. Thank you so much!
Oct 16 2013
parent "DoctorCaptain" <garble harble.com> writes:
 AWW you posted that while I was writing my latest novel. So 
 T[i] doesn't work? I guess I shouldn't have opened my eyes this 
 morning. In any case, typeof() DOES work, so as long as there 
 is a way to extract the type, we're good.

 I am extremely pleased it's actually possible to get individual 
 data members like I was originally attempting to do. I'm likely 
 not going to actually do this, as your T members; solution is 
 cleaner, but I'm glad it's not just arbitrarily impossible.

 Thank you so much!
And I could have SWORN I tried to write something like what you have in your latest DPaste, but I must have been doing something wrong, of course. In any case, I'm glad this whole thing has been so very cleanly resolved. Infinity thank you.
Oct 16 2013
prev sibling parent reply "DoctorCaptain" <garble harble.com> writes:
On Wednesday, 16 October 2013 at 14:08:52 UTC, Benjamin Thaut 
wrote:
 Am 16.10.2013 10:40, schrieb DoctorCaptain:
 http://dpaste.dzfl.pl/07b20d75

 Note the use of typeof() to get the type of the elements at 
 each index
 of members, to generate a type on which a constructor can be 
 called to
 instantiate the elements at each index of members. Magic.
There is actually a easier way to instanciate the elements. Just do "new T[i]();" no need for typeof.
Thank you again for your excellent answers. I knew I shouldn't have posted my most recent response at two in the morning or whatever it was; when I woke up this morning I was thinking, "I understand everything I was confused about last night, and I'm going to look silly to the fellas that answer my questions." Especially, pretty much the first thing I thought of when I opened my eyes this morning was "I don't need to do typeof, I can index the type tuple, and now I look silly." I definitely knew that the T in the mixed-in class definition is the same as the variadic template parameter T in our GrabBagT template. My question, at the time, was geared towards, "How in the world does it act like a tuple of types that we can just use?" The answer to that is, painfully obviously, T is literally a tuple of types that we can just use, working exactly as intended. Hopefully the following is correct, as an exercise in making sure that my understanding is correct: Purely theoretically speaking (as in we're no longer talking about D specifically, but rather "type theory" in general), let's take the type int[]. Variables of type int[] can be created. Let's do this: int[] iarray; For some within-bounds index i, iarray[i] will yield a -value- whose -type- is int. Along these same lines (again in a go-with-me-here, theoretical sense), if we were to "index" the actual type int[], like int[i], then what is yielded at that index is the -type- int. int[] is a homogenous type tuple of some length, containing only the -type- int as an element at each of its indexes. Go with me here. This works perfectly well. At compile time, the compiler knows everything it needs to know about int[] in order to reason about how a variable of that type, like iarray, can behave. It knows that at every within-bounds index of iarray, it can be sure to find a value of type int, because int[] is a homogenous type tuple of the type int. In exactly this same way, T is also a type tuple. The only difference is, T can be (but by no means has to be) a heterogenous type tuple. That is, since we're instantiating these templates (that take T...) at compile time, all types contained within T are known at compile time, and can be reasoned about. So let's say we declare something like: T members; instantiated such that myTemplate(T...) is called like myTemplate!(int, float, bool) This means that, while at int[0] we can expect the -type- int, and at int[1] we can still expect the -type- int, at T[0] we expect the -type- int, at T[1] we expect the -type- float, and at T[2] we expect the -type- bool. So, at members[0], we can store a -value- of type int, at members[1] we can store a -value- of type float, and at members[2], we can store a -value- of type bool. The reason this is possible is because the compiler is aware of all of the types, and their order, in the type tuple T at compile time. The compiler can reason about the behavior of any index within members, because it can map that index within members back to the same index within T. As long as members[n] is treated as the type T[n], the compiler can reason about the behavior of a heterogenous "array" members, of type T. Correct? And again again again, thank you for your help.
Oct 16 2013
parent Benjamin Thaut <code benjamin-thaut.de> writes:
Am 16.10.2013 21:02, schrieb DoctorCaptain:
 On Wednesday, 16 October 2013 at 14:08:52 UTC, Benjamin Thaut wrote:
 Am 16.10.2013 10:40, schrieb DoctorCaptain:
 http://dpaste.dzfl.pl/07b20d75

 Note the use of typeof() to get the type of the elements at each index
 of members, to generate a type on which a constructor can be called to
 instantiate the elements at each index of members. Magic.
There is actually a easier way to instanciate the elements. Just do "new T[i]();" no need for typeof.
Thank you again for your excellent answers. I knew I shouldn't have posted my most recent response at two in the morning or whatever it was; when I woke up this morning I was thinking, "I understand everything I was confused about last night, and I'm going to look silly to the fellas that answer my questions." Especially, pretty much the first thing I thought of when I opened my eyes this morning was "I don't need to do typeof, I can index the type tuple, and now I look silly." I definitely knew that the T in the mixed-in class definition is the same as the variadic template parameter T in our GrabBagT template. My question, at the time, was geared towards, "How in the world does it act like a tuple of types that we can just use?" The answer to that is, painfully obviously, T is literally a tuple of types that we can just use, working exactly as intended. Hopefully the following is correct, as an exercise in making sure that my understanding is correct: Purely theoretically speaking (as in we're no longer talking about D specifically, but rather "type theory" in general), let's take the type int[]. Variables of type int[] can be created. Let's do this: int[] iarray; For some within-bounds index i, iarray[i] will yield a -value- whose -type- is int. Along these same lines (again in a go-with-me-here, theoretical sense), if we were to "index" the actual type int[], like int[i], then what is yielded at that index is the -type- int. int[] is a homogenous type tuple of some length, containing only the -type- int as an element at each of its indexes. Go with me here. This works perfectly well. At compile time, the compiler knows everything it needs to know about int[] in order to reason about how a variable of that type, like iarray, can behave. It knows that at every within-bounds index of iarray, it can be sure to find a value of type int, because int[] is a homogenous type tuple of the type int. In exactly this same way, T is also a type tuple. The only difference is, T can be (but by no means has to be) a heterogenous type tuple. That is, since we're instantiating these templates (that take T...) at compile time, all types contained within T are known at compile time, and can be reasoned about. So let's say we declare something like: T members; instantiated such that myTemplate(T...) is called like myTemplate!(int, float, bool) This means that, while at int[0] we can expect the -type- int, and at int[1] we can still expect the -type- int, at T[0] we expect the -type- int, at T[1] we expect the -type- float, and at T[2] we expect the -type- bool. So, at members[0], we can store a -value- of type int, at members[1] we can store a -value- of type float, and at members[2], we can store a -value- of type bool. The reason this is possible is because the compiler is aware of all of the types, and their order, in the type tuple T at compile time. The compiler can reason about the behavior of any index within members, because it can map that index within members back to the same index within T. As long as members[n] is treated as the type T[n], the compiler can reason about the behavior of a heterogenous "array" members, of type T. Correct? And again again again, thank you for your help.
I never had type theory in university, but your explanation sounds reasonable and correct ;-) Kind Regards Benjamin Thaut
Oct 16 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 10/15/13 10:03, DoctorCaptain wrote:
 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'm not sure what exactly you're trying to do, but... The issue is just that you are trying to access types that are not available inside the GrabBagT template. With /built-in/ types this of course works, as 'int' etc are always known. One way to deal with that would be: template GrabBagT(string className, T...) if (allSatisfy!(isChildOrBaseClass, T)) { mixin(genClassStr!(T.length)(className)); } string genClassStr(size_t TL)(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; 0..TL) classStr ~= "T["~ to!(string)(i) ~ `] t` ~ to!(string)(i) ~ `;`; classStr ~= ` void printAttempts() {`; foreach (i; 0..TL) classStr ~= ` writeln(` ~ to!string(i) ~ `);`; classStr ~= ` }`; classStr ~= `}`; return classStr; } artur
Oct 15 2013