Template with template?

John Colvin john.loughran.colvin at gmail.com
Thu Mar 20 13:20:26 PDT 2014


On Thursday, 20 March 2014 at 19:38:25 UTC, Chris wrote:
> On Thursday, 20 March 2014 at 18:54:30 UTC, John Colvin wrote:
>> On Thursday, 20 March 2014 at 18:39:32 UTC, Chris wrote:
>>> On Thursday, 20 March 2014 at 17:49:52 UTC, John Colvin wrote:
>>>> On Thursday, 20 March 2014 at 16:40:50 UTC, Chris wrote:
>>>>> On Thursday, 20 March 2014 at 16:32:34 UTC, Vladimir 
>>>>> Panteleev wrote:
>>>>>> On Thursday, 20 March 2014 at 16:28:46 UTC, Chris wrote:
>>>>>>> How can I instantiate Person with Trait, i.e. a template 
>>>>>>> with a template?
>>>>>>>
>>>>>>> struct Trait(T0, T1) {
>>>>>>> T0 name;
>>>>>>> T1 value;
>>>>>>> T1[T0] map;
>>>>>>>
>>>>>>> this(T0 name, T1 value) {
>>>>>>> this.name = name;
>>>>>>> this.value = value;
>>>>>>> map[name] = value;
>>>>>>> }
>>>>>>> }
>>>>>>>
>>>>>>> class Person(T) {
>>>>>>> T traits[];
>>>>>>>
>>>>>>> void addTrait(T trait) {
>>>>>>> traits ~= trait;
>>>>>>> }
>>>>>>> }
>>>>>>>
>>>>>>>
>>>>>>> void main()
>>>>>>> {
>>>>>>> auto trait1 = Trait!(string, string)("Name", "John");
>>>>>>> auto trait2 = Trait!(string, int)("Age", 42);
>>>>>>> writefln(%s", trait1.map);
>>>>>>> writefln(%s", trait2.map);
>>>>>>> // above code compiles and works
>>>>>>> }
>>>>>>
>>>>>> Person!(Trait!(string, string)) person;
>>>>>>
>>>>>> -- or --
>>>>>>
>>>>>> alias MyTrait = Trait!(string, string);
>>>>>> Person!MyTrait person;
>>>>>>
>>>>>> Note that this approach won't let you have traits with 
>>>>>> different parameters within the same Person type.
>>>>>
>>>>> Yep, I've already tried this (sorry I should've mentioned 
>>>>> it!). But I don't want this restriction.
>>>>
>>>> Arrays are homogeneous. All the elements must be of the same 
>>>> type. Different instantiations of templates are different 
>>>> types.
>>>>
>>>> You could use an array of std.variant.Variant
>>>
>>> The elements are all of type Trait. However, Type itself 
>>> might be
>>> of different types. That's why it is not possible? I've come
>>> across this restriction before when using templates, which is 
>>> a
>>> big disappointment because it restricts the "templatization" /
>>> generalization of data structures somewhat.
>>
>> Trait is not a type. Trait is a template. An instantiation of 
>> the Trait template is a type.
>>
>>
>> Arrays are contiguous, homogeneous data. This is fundamental 
>> to their design and their performance characteristics.
>> Workarounds use at least one of the following: indirection, 
>> tagging* and padding. Variant uses tagging and padding. 
>> Interface/base-class arrays use indirection (and tagging, 
>> ultimately).
>>
>> *inline or external, or even compile-time.
>>
>> This is true in *every* programming language, just with 
>> different names.
>
> I thought the array T[] traits could hold any _type_ the 
> template Trait is instantiated into. That's where I got it 
> wrong. I understand the practical aspects of this restriction 
> (homogeneous data, performance and the work around involved 
> etc.). However, this makes templates less universal and rather 
> cumbersome to work with in certain circumstances. Take for 
> example the Person class. If I want to do numerical operations 
> with the age of the person, I will have to convert the age to 
> an int (or whatever) later on instead of just doing it once at 
> the beginning (when loading data). So everytime I access 
> Trait.map["age"] I will have to convert it to a number before I 
> can calculate anything. This, or I store it in a field of its 
> own when instantiating Trait. Whatever workaround I choose it 
> will make it less elegant and less simple.
>
> Maybe I expect(ed) to much of templates. Mea culpa.

Try this:

import std.stdio;
import std.variant;

enum maxTraitSize = 64;

struct Trait(T0, T1)
{
	T0 name;
	T1 value;
	T1[T0] map;

	static assert(T0.sizeof + T1.sizeof + (T1[T0]).sizeof <= 
maxTraitSize);
	
	this(T0 name, T1 value)
	{
		this.name = name;
		this.value = value;
		map[name] = value;
	}
}

class Person
{
	alias ElT = VariantN!maxTraitSize;
	ElT[] traits;

	void addTrait(T)(T trait)
		if(is(T == Trait!Q, Q...))
	{
		traits ~= ElT(trait);
	}
}

void main()
{
	auto trait1 = Trait!(string, string)("Name", "John");
	auto trait2 = Trait!(string, int)("Age", 42);
	writefln("%s", trait1.map);
	writefln("%s", trait2.map);
	
	auto p = new Person;
	p.addTrait(trait1);
	p.addTrait(trait2);
	writeln(p.traits);
}


More information about the Digitalmars-d-learn mailing list