October 24, 2012

Merhaba,

Bakınız adamlar neler yapmışlar! Herhangi bir döngü yok ve bildirilen enum'un tüm üyelerini çokuzlu olarak geri döndürüyor. Bunu nasıl yaptığını tam olarak anlayamasam da özyinelemeli olarak şablonu çağırdığını ve dilim kullanarak tek tek enum üzerinde gezdiğini zannediyorum.

Alıntı:

>
> template EnumSpecificMembers(names...) {
>         static if (names.length > 0) {
>             alias TypeTuple!(
>                     WithIdentifier!(names[0])
>                         .Symbolize!(__traits(getMember, E, names[0])),
>                     EnumSpecificMembers!(names[1 .. $])
>                 ) EnumSpecificMembers;
>         :        :        :
> ```

>

Orijinali şurada (<https://github.com/D-Programming-Language/phobos/blob/master/std/traits.d#L2324>) ve şablonun tamamı unittest'leri ile birlikte aşağıda...:)

template EnumMembers(E)
if (is(E == enum))
{
// Supply the specified identifier to an constant value.
template WithIdentifier(string ident)
{
static if (ident == "Symbolize")
{
template Symbolize(alias value)
{
enum Symbolize = value;
}
}
else
{
mixin("template Symbolize(alias "~ ident ~")"
~"{"
~"alias "~ ident ~" Symbolize;"
~"}");
}
}

template EnumSpecificMembers(names...)
{
static if (names.length > 0)
{
alias TypeTuple!(
WithIdentifier!(names[0])
.Symbolize!(__traits(getMember, E, names[0])),
EnumSpecificMembers!(names[1 .. $])
) EnumSpecificMembers;
}
else
{
alias TypeTuple!() EnumSpecificMembers;
}
}

alias EnumSpecificMembers!(__traits(allMembers, E)) EnumMembers;
}

unittest
{
enum A { a }
static assert([ EnumMembers!A ] == [ A.a ]);
enum B { a, b, c, d, e }
static assert([ EnumMembers!B ] == [ B.a, B.b, B.c, B.d, B.e ]);
}

unittest // typed enums
{
enum A : string { a = "alpha", b = "beta" }
static assert([ EnumMembers!A ] == [ A.a, A.b ]);

static struct S
{
int value;
int opCmp(S rhs) const nothrow { return value - rhs.value; }
}
enum B : S { a = S(1), b = S(2), c = S(3) }
static assert([ EnumMembers!B ] == [ B.a, B.b, B.c ]);
}



-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
October 24, 2012

Of! Gerçekten ilginç. :) Bu işte o kadar çok olanağın parmağı geçiyor ki, hangisinden başlasam bilemedim. :)

İşin püf noktası şu: enum değerlerinin isimlerinden oluşan bir tür listesi (TypeTuple) oluşturuyorlar. Bunu yaparken bir çok sıkıntı var:

  • enum değerlerinin isim hali bir tür değildir. Onu aşmak için WithIdentifier diye bir aracı tür kullanıyorlar. Bu aracı türün içindeki bir alias, enum'un değerinin isminden bir tür tanımlamış oluyor. Onları bir araya getirerek de bir tür listesi oluşturuyor. Tabii enum değerleri gerçek türlere karşılık gelmiyorlar ama bundan TypeTuple'un haberi yok. O yalnızca daha sonra kullanılabilsin diye tür isimleri biriktiriyor.

Basitleştirilmiş olarak şöyle görebiliriz:

import std.stdio;
import std.typetuple;

template Symbolize(alias değer)
{
   enum Symbolize = değer;
}

void main()
{
   /* TypeTuple normalde tür listesi oluşturmak içindir: */
   foreach (tür; TypeTuple!(int, double)) {
       auto değişken = tür.init;
       writefln("%s değişken: %s", tür.stringof, değişken);
   }

   /* Symbolize numarası ile dizgileri de türmüş gibi bir araya
    * getirebiliyoruz.
    *
    * TypeTuple'un dilim içine yazıldığına ayrıca dikkat. O da daha önce
    * konuştuğumuz başka bir kolaylıktı. */
   auto yapmaTürler = [ TypeTuple!(Symbolize!"a", Symbolize!"b") ];
   writeln(yapmaTürler);

   /* İşin güzeli, bir enum'un değerlerinin string karşılıkları bile tür
    * listesi olarak kullanılabiliyor: */
   enum E { bir, iki }
   auto enumDeğerleri = [ TypeTuple!(Symbolize!(E.bir), Symbolize!(E.iki)) ];
   writeln(enumDeğerleri);
}
  • Başka bir güçlük, enum'un bütün değerlerinde teker teker gezinmek ve onlardan sözde tür isimleri üretmek. Bunun için iki araçtan yararlanılıyor:

** İlk araç, bütün üyeleri veren __traits(allMembers). Ancak, bu değerlerden oluşan bir dilim oluşturur. O değerleri tür isimlerine dönüştürmek için yukarıdaki düzenekten yararlanılıyor.

** İkinci araç, C++'ta da çok uygulanan bir yöntem: şablonlarla derleme zamanında özyineleme.

Bu özyinelemeyi şöyle tanıyoruz: names[0]'ın ismi ile names[1..$]'ın isimleri bir araya getiriliyor. Ondan sonra, bunlardan oluşan bir TypeTuple oluşturuluyor. Şablon özyinelemesini TypeTuple!() ile belirtilen boş TypeTuple sonlandırıyor.

Şablon özyinelemesini gösteren basit bir örnek. 0'dan verilen değere kadar olan sayıların toplamını hesaplıyor:

import std.stdio;
import std.array;

template toplam(long N)
{
   static if (N == 0) {
       enum toplam = 0;

   } else {
       enum toplam = N + toplam!(N - 1);
   }
}

void main()
{
   writeln(toplam!10);
}

0'dan 10'a kadar sayıların toplamı 55'tir. Üretilen koda baktığımızda 55 değerinin koda gömülmüş olduğunu görüyoruz (onaltılı 37, 55'tir):

		mov	RDI,037h
		call	  _D3std5stdio14__T7writelnTlZ7writelnFlZv@PC32

İşte Salih'in gösterdiği kod bu olanaklardan yararlanıyor.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]