Is there a way to set an alias to specific form of a template?

Ali Çehreli acehreli at yahoo.com
Sat Oct 8 09:58:01 PDT 2011


On Sat, 08 Oct 2011 03:52:25 -0700, Roderick Gibson wrote:

> On 10/7/2011 11:35 PM, Ali Çehreli wrote:
>> On Fri, 07 Oct 2011 18:29:26 -0700, Roderick Gibson wrote:
>>
>>> This may be the completely wrong approach, but I am basically thinking
>>> of something like this (I am aware this will not compile, it's
>>> psuedocode):
>>>
>>> class Vector(T) {
>>> 	... //definition here
>>> }
>>>
>>> alias Vector(float, float) vec2f;
>>> auto v = new vec2f(1.0,1.0);
>>>
>>> I am making a templated Vector class (a mathematical vector) that will
>>> have varying types (thus a template) and dimensions (via variadic
>>> functions), so that the same template definition will work for 2d or
>>> 3d vectors (or 4d, etc).
>>>
>>> I then want the programmer to be able to define the specific forms
>>> that he wants so he can easily keep track of them (without getting
>>> confused about which is a 2d integer vector and which is a 3d float
>>> vector), and then use those forms in a type safe manner. Is this even
>>> possible? If it is, but it's the wrong way to do it, what's the right
>>> way?
>>>
>>> Basically I wanted to write it once and not worry about writing it
>>> again to handle different types and dimensions (no vec2i class, or
>>> vec2f, or vec3f, or vec3i, etc). Templates easily handles the type
>>> requirement, but what about the dimensional requirement? Am I just
>>> going to have to rewrite it when I add dimensions?
>>
>> You can take advantage of 'Template Value Parameters' and 'Typesafe
>> Variadic Functions':
>>
>>    http://www.d-programming-language.org/
>> template.html#TemplateValueParameter
>>
>>    http://www.d-programming-language.org/function.html
>>
>> class Vector(T, int N)
>> {
>>      T[N] elements;
>>
>>      this(T[] elements ...)
>>      {
>>          this.elements = elements;
>>      }
>> }
>>
>> alias Vector!(double, 2) Vec2D;
>> alias Vector!(double, 3) Vec3D;
>>
>> void main()
>> {
>>      auto v2d = new Vec2D(2.2, 2.2);
>>      auto v3d = new Vec3D(3.3, 3.3, 3.3);
>>
>>      // Alternatively, all parameters at once: auto v3d_too = new
>>      Vec3D([ 33, 33, 33, ]);
>> }
>>
>> (Some would find 'size_t N' to be more appropriate since N is a
>> dimension.)
>>
>> Ali
> 
> I decided this would be the best way, thank you. One question though, I
> noticed with this method that you can only assert that the dimension and
> the parameter list length match at runtime (ie, someone could
> instantiate a vec2d as vec2d(2.2, 2.2, 3.1) and the compiler will
> happily accept it), I'm guessing constraints are what's needed, but I
> don't know how to get the parameter count at compile time, only at
> runtime (via elements.length). The compiler *should* know the length at
> compile time shouldn't it?
> 
> I managed to get it to at least stop the compilation with
> 
> this(T[N] elements...)

I didn't know that would work. :)

> but the error messages are terrible. Is there a better way, perhaps
> using a static assert?

Here is some ugly code that accepts either T[N] or N Ts. Note that the 
constructor now takes T[N], not T[N]...:

import std.stdio;
import std.string;
import std.conv;

string ctor_with_params(T, int N)()
{
    string param_list;
    foreach (i; 0 .. N) {
        if (i != 0) {
            param_list ~= ", ";
        }
        param_list ~= T.stringof ~ " p" ~ to!string(i);
    }

    string code = "this(" ~ param_list ~ ") { elements = [ " ;

    foreach (i; 0 .. N) {
        if (i != 0) {
            code ~= ", ";
        }
        code ~= "p" ~ to!string(i);
    }

    code ~= " ]; }";

    return code;
}

unittest
{
    writeln(ctor_with_params!(double, 3)());
}

class Vector(T, int N)
{
    T[N] elements;

    this(T[N] elements)
    {
        this.elements = elements;
    }

    mixin(ctor_with_params!(T, N)());
}

alias Vector!(double, 3) Vec3D;

void main()
{
    auto v0 = new Vec3D(  33, 33, 33,  );
    auto v1 = new Vec3D([ 33, 33, 33, ]);

    /*
      Error: constructor deneme.Vector!(double,3).Vector.this (double[3LU]
      elements) is not callable using argument types (int,int)
    */
    // auto v2 = new Vec3D(  33, 33  );


    /*
      Error: constructor deneme.Vector!(double,3).Vector.this (double[3LU]
      elements) is not callable using argument types (int[])

      Error: cannot implicitly convert expression ([33,33,33,33]) of type 
int[]
      to double

      Error: expected 3 function arguments, not 1
    */
    // auto v3 = new Vec3D([ 33, 33, 33, 33 ]);
}

It prints the mixed-in N-parameter constructor:

this(double p0, double p1, double p2) { elements = [ p0, p1, p2 ]; }

If you don't need the T[N] constructor at all, the best approach could be 
removing it. Then the error messages make more sense:

class Vector(T, int N)
{
    T[N] elements;

    mixin(ctor_with_params!(T, N)());
}

Ali


More information about the Digitalmars-d-learn mailing list