Template with template?
Chris
wendlec at tcd.ie
Fri Mar 21 02:54:22 PDT 2014
On Thursday, 20 March 2014 at 20:20:28 UTC, John Colvin wrote:
> 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);
> }
Thanks John, this does what I had in mind. I don't know, though,
if I will use it for a real world application. I would have to
test the behavior thoroughly first. The background is that I will
have variable user input, i.e. users (non-programmers) define
traits and rules. There is no way to foresee what names users
will choose. That's why Trait stores arbitrary keys and values
(and Meta's solution is not an option). I only thought it would
be nice to have [string:int] straight away for numerical
operations further down the road. I was also thinking about using
std.variant. But I'm just starting out with this ...
More information about the Digitalmars-d-learn
mailing list