Thread overview
Array initialization with Struct templates
Aug 31, 2015
WhatMeWorry
Aug 31, 2015
Jonathan M Davis
Aug 31, 2015
Ali Çehreli
Aug 31, 2015
Mike Parker
Aug 31, 2015
Daniel N
Sep 01, 2015
WhatMeWorry
Sep 01, 2015
Ali Çehreli
August 31, 2015
This seemingly trivial array initialization has caused me hours of grief.

enum Purpose { POSITIONAL, COLOR_ONLY, COLOR_AND_ALPHA, GENERIC_TRIPLE, GENERIC_QUAD }
Purpose purpose;

struct Chameleon(T, Purpose p)  // template
{
    static if (is (p == POSITIONAL)) {
        T  x, y, z;
    } else static if (is (p == COLOR_ONLY)) {
        T  r, g, b;
    } else static if (is (p == COLOR_AND_ALPHA)) {
        T  r, g, b, a;
    }  else static if (is (p == GENERIC_TRIPLE)) {
        T  a, b, c;
    } else static if (is (p == GENERIC_QUAD)) {
        T  a, b, c, d;
    }
};

struct VertexData
{
    Chameleon!(float, purpose.POSITIONAL)  position;
    Chameleon!(float, purpose.COLOR_ONLY)  color;
}

alias Vert = VertexData;

VertexData[] vertices =
[
    Vert(1.0, 1.0, 1.0, 0.0, 0.0, 0.0)  // compiler error here
];

I keep getting:

Error: cannot implicitly convert expression (1.00000) of type double to Chameleon!(double, cast(Purpose)0)	

I even tried  Vert(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f)

but it has the exact same error.  Any ideas?  Thanks in advance.


August 31, 2015
On Monday, August 31, 2015 04:57:05 WhatMeWorry via Digitalmars-d-learn wrote:
>
> This seemingly trivial array initialization has caused me hours of grief.
>
> enum Purpose { POSITIONAL, COLOR_ONLY, COLOR_AND_ALPHA,
> GENERIC_TRIPLE, GENERIC_QUAD }
> Purpose purpose;
>
> struct Chameleon(T, Purpose p)  // template
> {
>      static if (is (p == POSITIONAL)) {
>          T  x, y, z;
>      } else static if (is (p == COLOR_ONLY)) {
>          T  r, g, b;
>      } else static if (is (p == COLOR_AND_ALPHA)) {
>          T  r, g, b, a;
>      }  else static if (is (p == GENERIC_TRIPLE)) {
>          T  a, b, c;
>      } else static if (is (p == GENERIC_QUAD)) {
>          T  a, b, c, d;
>      }
> };
>
> struct VertexData
> {
>      Chameleon!(float, purpose.POSITIONAL)  position;
>      Chameleon!(float, purpose.COLOR_ONLY)  color;
> }
>
> alias Vert = VertexData;
>
> VertexData[] vertices =
> [
>      Vert(1.0, 1.0, 1.0, 0.0, 0.0, 0.0)  // compiler error here
> ];
>
> I keep getting:
>
> Error: cannot implicitly convert expression (1.00000) of type
> double to Chameleon!(double, cast(Purpose)0)
>
> I even tried  Vert(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f)
>
> but it has the exact same error.  Any ideas?  Thanks in advance.

VertexData doesn't have a constructor that takes 6 doubles or 6 floats. It has a compiler-generated constructor that's equivalent to


this(Chameleon!(float, purpose.POSITIONAL) position,
     Chameleon!(float, purpose.COLOR_ONLY) color)
{
    this.position = position;
    this.color = color;
}

So, you're going to need to pass it a Chameleon!(float, purpose.POSITIONAL) and a Chameleon!(float, purpose.COLOR_ONLY color), not 6 doubles - either that, or you're going to need to declare a constructor for VertexData which takes 6 doubles or floats and converts them to what's require to assign to its member variables.

- Jonathan M Davis

August 31, 2015
On 08/30/2015 10:38 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
> On Monday, August 31, 2015 04:57:05 WhatMeWorry via Digitalmars-d-learn wrote:
>>
>> This seemingly trivial array initialization has caused me hours
>> of grief.
>>
>> enum Purpose { POSITIONAL, COLOR_ONLY, COLOR_AND_ALPHA,
>> GENERIC_TRIPLE, GENERIC_QUAD }
>> Purpose purpose;
>>
>> struct Chameleon(T, Purpose p)  // template
>> {
>>       static if (is (p == POSITIONAL)) {
>>           T  x, y, z;
>>       } else static if (is (p == COLOR_ONLY)) {
>>           T  r, g, b;
>>       } else static if (is (p == COLOR_AND_ALPHA)) {
>>           T  r, g, b, a;
>>       }  else static if (is (p == GENERIC_TRIPLE)) {
>>           T  a, b, c;
>>       } else static if (is (p == GENERIC_QUAD)) {
>>           T  a, b, c, d;
>>       }
>> };
>>
>> struct VertexData
>> {
>>       Chameleon!(float, purpose.POSITIONAL)  position;
>>       Chameleon!(float, purpose.COLOR_ONLY)  color;
>> }
>>
>> alias Vert = VertexData;
>>
>> VertexData[] vertices =
>> [
>>       Vert(1.0, 1.0, 1.0, 0.0, 0.0, 0.0)  // compiler error here
>> ];
>>
>> I keep getting:
>>
>> Error: cannot implicitly convert expression (1.00000) of type
>> double to Chameleon!(double, cast(Purpose)0)
>>
>> I even tried  Vert(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f)
>>
>> but it has the exact same error.  Any ideas?  Thanks in advance.
>
> VertexData doesn't have a constructor that takes 6 doubles or 6 floats. It
> has a compiler-generated constructor that's equivalent to
>
>
> this(Chameleon!(float, purpose.POSITIONAL) position,
>       Chameleon!(float, purpose.COLOR_ONLY) color)
> {
>      this.position = position;
>      this.color = color;
> }
>
> So, you're going to need to pass it a Chameleon!(float, purpose.POSITIONAL)
> and a Chameleon!(float, purpose.COLOR_ONLY color), not 6 doubles - either
> that, or you're going to need to declare a constructor for VertexData which
> takes 6 doubles or floats and converts them to what's require to assign to
> its member variables.
>
> - Jonathan M Davis
>

Additionally, the OP uses the is expression which compares the equality of types. However, 'Purpose p' is a value template parameter. A simple == comparison works:

enum Purpose { POSITIONAL, COLOR_ONLY, COLOR_AND_ALPHA, GENERIC_TRIPLE, GENERIC_QUAD }
Purpose purpose;

struct Chameleon(T, Purpose p)  // template
{
    static if (p == Purpose.POSITIONAL) {  // <-- NOT is expression
        T  x, y, z;
    } else static if (p == Purpose.COLOR_ONLY) {
        T  r, g, b;
    } else static if (p == Purpose.COLOR_AND_ALPHA) {
        T  r, g, b, a;
    }  else static if (p == Purpose.GENERIC_TRIPLE) {
        T  a, b, c;
    } else static if (p == Purpose.GENERIC_QUAD) {
        T  a, b, c, d;
    }
};

struct VertexData
{
    Chameleon!(float, purpose.POSITIONAL)  position;
    Chameleon!(float, purpose.COLOR_ONLY)  color;
}

alias Vert = VertexData;

VertexData[] vertices =
[
    Vert(Chameleon!(float, purpose.POSITIONAL)(1.0f, 1.0f, 1.0f),
         Chameleon!(float, purpose.COLOR_ONLY)(0.0, 0.0, 0.0))
];

void main()
{}



August 31, 2015
On Monday, 31 August 2015 at 05:47:31 UTC, Ali Çehreli wrote:
> On 08/30/2015 10:38 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
>> On Monday, August 31, 2015 04:57:05 WhatMeWorry via Digitalmars-d-learn wrote:
>>>
>>> This seemingly trivial array initialization has caused me hours
>>> of grief.
>>>
>>> enum Purpose { POSITIONAL, COLOR_ONLY, COLOR_AND_ALPHA,
>>> GENERIC_TRIPLE, GENERIC_QUAD }
>>> Purpose purpose;
>>>
>>> struct Chameleon(T, Purpose p)  // template
>>> {
>>>       static if (is (p == POSITIONAL)) {
>>>           T  x, y, z;
>>>       } else static if (is (p == COLOR_ONLY)) {
>>>           T  r, g, b;
>>>       } else static if (is (p == COLOR_AND_ALPHA)) {
>>>           T  r, g, b, a;
>>>       }  else static if (is (p == GENERIC_TRIPLE)) {
>>>           T  a, b, c;
>>>       } else static if (is (p == GENERIC_QUAD)) {
>>>           T  a, b, c, d;
>>>       }
>>> };
>>>
>>> struct VertexData
>>> {
>>>       Chameleon!(float, purpose.POSITIONAL)  position;
>>>       Chameleon!(float, purpose.COLOR_ONLY)  color;
>>> }
>>>
>>> alias Vert = VertexData;
>>>
>>> VertexData[] vertices =
>>> [
>>>       Vert(1.0, 1.0, 1.0, 0.0, 0.0, 0.0)  // compiler error here
>>> ];

I would drop chameleon all together and just add the fields directly to VertexData, but make Purpose flag-based.

enum Purpose {
   position = 0x00,   // Assuming all verts have a position
   colorOnly = 0x01,
   colorAlpha = 0x02,
   triple = 0x04,
   quad = 0x08
}

enum hasColorOnly(Purpose p) = (p & Purpose.colorOnly) == Purpose.colorOnly;
struct VertexData(T, Purpose purpose){
   T x, y, z;
   static if(hasColorOnly!purpose)
      T r, g, b;
   ...
}

Then you can templatize your batches or meshes, or whatever you use to represent vertex objects, on the VertexData type.

alias ColorVertexf = VertexData!(float, Purpose.colorOnly);
auto batch = new Batch!(ColorVertexf);



August 31, 2015
On Monday, 31 August 2015 at 05:38:54 UTC, Jonathan M Davis wrote:
> So, you're going to need to pass it a Chameleon!(float, purpose.POSITIONAL) and a Chameleon!(float, purpose.COLOR_ONLY color), not 6 doubles - either that, or you're going to need to declare a constructor for VertexData which takes 6 doubles or floats and converts them to what's require to assign to its member variables.
>
> - Jonathan M Davis

Or turn Chameleon into a mixin template.

enum Purpose { POSITIONAL, COLOR_ONLY, COLOR_AND_ALPHA, GENERIC_TRIPLE, GENERIC_QUAD }
Purpose purpose;

mixin template Chameleon(T, Purpose p) // mixin template
{
    static if (p == Purpose.POSITIONAL) {  // <-- NOT is expression
        T  x, y, z;
    } else static if (p == Purpose.COLOR_ONLY) {
        T  r, g, b;
    } else static if (p == Purpose.COLOR_AND_ALPHA) {
        T  r, g, b, a;
    }  else static if (p == Purpose.GENERIC_TRIPLE) {
        T  a, b, c;
    } else static if (p == Purpose.GENERIC_QUAD) {
        T  a, b, c, d;
    }
};

struct VertexData
{
  mixin Chameleon!(float, purpose.POSITIONAL) position;
  mixin Chameleon!(float, purpose.COLOR_ONLY) color;
}

alias Vert = VertexData;

VertexData[] vertices =
[
    Vert(1.0f, 1.0f, 1.0f, 0.0, 0.0, 0.0),
];

void main()
{}
September 01, 2015
Thanks for all the above suggestions, but after many hour of re-reading Ali's book on template, structs, and mixins, I still in the woods.  I've tried two approaches:

================ Templatetized struct ================================

struct Chameleon(T, Purpose p)
{
    static if (p == Purpose.POSITIONAL)
    {
        struct Positional { T  x, y, z; }
    }
    else static if (p == Purpose.COLOR)
    {
        struct Color_No_Alpha { T  r, g, b; }
    }
};

struct VertexData
{
    Chameleon!(float, purpose.POSITIONAL)  position;
    Chameleon!(float, purpose.COLOR)  color;
}

alias Vert = VertexData;
alias Pos = VertexData.position;
alias RGB = VertexData.color;

VertexData[] vertices =
[
    VertexData( Pos(1.0, 1.0, 1.0), RGB(0.0, 0.0, 0.0))  // Compiler error
];

Error: struct Chameleon does not overload ()

Is this the overloading of the default constructor? Can't I use it?  I'm I calling it incorrectly?

	
======================  The Mixin Template approach ===========================

mixin template Chameleon(T, Purpose p)
{
    static if (p == Purpose.POSITIONAL)
    {
        struct Positional { T  x, y, z; }
    }
    else static if (p == Purpose.COLOR)
    {
        struct Color_No_Alpha { T  r, g, b; }
    }
};

struct VertexData
{
    mixin Chameleon!(float, purpose.POSITIONAL)  position;
    mixin Chameleon!(float, purpose.COLOR)  color;
}

alias Vert = VertexData;
alias Pos = VertexData.position;
alias RGB = VertexData.color;

VertexData[] vertices =
[
    VertexData( Pos(1.0, 1.0, 1.0), RGB(0.0, 0.0, 0.0))
];

returns  Error: function expected before (), not mixin Chameleon!(float, cast(Purpose)0) position;

And like above, I'm clueless.


September 01, 2015
On 08/31/2015 08:55 PM, WhatMeWorry wrote:

> Thanks for all the above suggestions, but after many hour of re-reading
> Ali's book on template, structs, and mixins, I still in the woods.  I've
> tried two approaches:
>
> ================ Templatetized struct ================================
>
> struct Chameleon(T, Purpose p)
> {
>      static if (p == Purpose.POSITIONAL)
>      {
>          struct Positional { T  x, y, z; }
>      }
>      else static if (p == Purpose.COLOR)
>      {
>          struct Color_No_Alpha { T  r, g, b; }
>      }
> };

(Aside: You don't need that semicolon in D.)

So, you have a struct template that contains another struct type, either a Positional or a Color_No_Alpha.

Note that the outer struct has no members at all: It merely defines two struct types. I can do the following but I don't think it's what you want:

auto x = Chameleon!(float, Purpose.POSITIONAL).Positional(1.5, 2.5, 3.5);

> struct VertexData
> {
>      Chameleon!(float, purpose.POSITIONAL)  position;
>      Chameleon!(float, purpose.COLOR)  color;
> }

You have a couple of typos there: purpose should be capitalized. However, I see that you had the following in your original code:

enum Purpose { POSITIONAL, COLOR_ONLY, COLOR_AND_ALPHA, GENERIC_TRIPLE, GENERIC_QUAD }
Purpose purpose;

// ...

    Chameleon!(float, purpose.POSITIONAL)  position;

I am surprised that it works! Must be a "feature". ;) The common thing to do is to remove the 'purpose' variable and use the enum type directly:

enum Purpose { /* ... */ }

// ...

    Chameleon!(float, Purpose.POSITIONAL)  position;

> alias Vert = VertexData;

Fine.

> alias Pos = VertexData.position;
> alias RGB = VertexData.color;

Those two don't make sense because both .position and .color are member variables. If you did the following, then we would be talking about the types of those members:

alias Pos = typeof(VertexData.position);
alias RGB = typeof(VertexData.color);

> VertexData[] vertices =
> [
>      VertexData( Pos(1.0, 1.0, 1.0), RGB(0.0, 0.0, 0.0))  // Compiler error
> ];

However, that won't work because Chameleon does not have any members.

The same with the mixin template...

I am copying my earlier code with a couple of aliases to make closer to yours:

enum Purpose { POSITIONAL, COLOR_ONLY }

struct Chameleon(T, Purpose p)
{
    static if (p == Purpose.POSITIONAL) {
        T  x, y, z;
    } else static if (p == Purpose.COLOR_ONLY) {
        T  r, g, b;
    } else {
        static assert(false, "Not yet supported");
    }
}

alias Pos = Chameleon!(float, Purpose.POSITIONAL);
alias Color = Chameleon!(float, Purpose.COLOR_ONLY);

struct VertexData
{
    Pos position;
    Color color;
}

alias Vert = VertexData;

VertexData[] vertices =
[
    Vert(Pos(1.0f, 1.0f, 1.0f), Color(0.0, 0.0, 0.0))
];

void main()
{}

Ali