On Friday, 18 September 2015 at 20:55:06 UTC, Adam wrote:
Fact: Templates are static!
But templates are still normal functioning code and can
actually work dynamically. We can see this by including a D
compiler inside an app and have the app re-compile the
templates for specific dynamic types. It doesn't need templates
to do of course, but it shows that "Templates are static" is
not quite as simple as it sounds.
But could D actually create some sort of internal expression of
D templates as dynamic objects that can be used in run-time?
If have a template T that only uses 3 types, then one can
simple do something like(pseudo):
t(object o)
{
// use dynamic reflection or whatever to get the type of the
object o
if (o.type == 'int')
return T!(int)(o.value, o.unit);
if (o.type == 'float')
return T!(float)(o.value, o.unit);
return T!(unknown)(o.value, o.unit);
}
etc...
t then is a "dynamic representation" of the static
functionality of T.
But my guess is that the compiler could essentially do this for
us and allow one to use templates in a dynamic way. Most of the
time it may not be of much use but, say, if one is interacting
with console and T converts a string to a number. T could
convert a number with unit such as "3mm" in to some base
representation such as 0.003(m). float's are handled
differently than ints(rounding depending on some criteria).
Maybe we use the T internally and its lightning fast. But for
the console case, we have write a dynamic version of the
template or create something similar to the code above.
But the compiler already knows, at least, some of the times
that will be used and it should be able to instantiate the
template for each of those types.
e.g., if you use T!(float) in the code, it will know to create
the "dynamic wrapper" to include that type in the list. If you
don't every use T!(float) explicitly then you can simply create
a useless statement like {auto x = T!(float)("3.04mm");} which
will be optimized but the compiler will then include float as a
type used in the dynamic version of T. I may not be making much
sense here, but it's the best I'll do.
The compiler then can do something like assemble the dynamic
version of the template into a new function which can be used
at compile time on any object.
Using a simple special character could inform the compiler to
use the dynamic template version.
object u = userInput();
&T(u); // (instead of using t(u)).
Here the compiler does all the work for us. If u has type float
then this gets effectively dispatched to
T!(float)(cast(float)u). If it's an int, then
T!(int)(cast(int)u).
What it mainly does for us is simplify having to do the cast
our self along with the string of if checking for all the types
or having to code that works dynamically
Or is it impossible for the D compiler to accomplish such a
task automatically for us?
I think that what you're asking can only be accomplished in an
interpreted or JIT compiled language. With a compiled language,
you have to know all the possible types you will support up front.
If you're getting the input from a console though, you must be
parsing it right? The parser cannot complete it's task without
knowing what it's parsing, and the parser should pass along or
store any relevant information for future use.
For compiled languages, there is the idea of "Type Erasure".
Polymorphism is one way to get this done. You will have to create
your own base object which specifies all possible functionality.
Example:
import std.stdio;
import std.conv;
import std.random;
abstract class BaseObj {
abstract void print();
}
class FloatObj : BaseObj {
float value;
this(float val) { value = val; }
override void print() { writeln("float: " ~ value.to!string);
}
}
class IntObj : BaseObj {
int value;
this(int val) { value = val; }
override void print() { writeln("int: " ~ value.to!string); }
}
BaseObj userInput()
{
if(uniform(0, 2) % 2 == 0)
return new FloatObj(uniform(0.0f, 100.0f));
else
return new IntObj(uniform(0, 100));
}
void main(string[] args)
{
BaseObj[] objs;
foreach(i; 0..10)
{
BaseObj obj = userInput();
obj.print();
}
}