Template with template?
Chris
wendlec at tcd.ie
Fri Mar 21 02:56:49 PDT 2014
On Friday, 21 March 2014 at 09:54:24 UTC, Chris wrote:
> 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 ...
Btw, I was initially inspired by Objective-C's NSSet that can
hold arbitrary objects.
More information about the Digitalmars-d-learn
mailing list