C++ Variadic Templates 2

Kerim Fırat
5 min readAug 28, 2022

C++ Variadic Templates makalesi için

İlk makalede “Variadic Template” özelliğini “recursive” ağırlıklı kullandık. Ancak Variadic template kullanırken “recursive” yanısıra “Fold” kavramı(C++17 itibariyle) da kullanılır. İleriki yazılarımda “Fold” kavramını daha farklı ve komplik template örnekleriyle işleyeceğiz. Ancak bu makalede Variadic özelliğini daha çok çeşitli örneklerle genişleteceğiz.
Bu bağlamda “recursive” ve “fold” kavramlarını ayrıca birlikte kullanacak ve farklarını inceleyeceğiz. Özellikle “fold” kavramı devreye girdiğinde,daha ilkel kalan recursive’a karşı Variadic template’in kullanım açısından yapısal fark getirdiğini göreceğiz.

NOT: Kavramı araştırmak isteyenler “Fold Expressions” kelimesiyle araştırabilirler.

Recursive vs Fold

Syntax açısından baktığımızda,aslında bu iki kavram arasında çok büyük bir fark göremeyiz. Ancak makalenin başından da belirttiğim gibi yapısal farklılıklar oluşuyor. Aşağıda iki örnek yer almaktadır. İlk örnek “recursive” kavramı,ikinci örnekde ise “Fold” kavramı kullanılıyor.
Örnek1:

Outpt:
10 20 30 item 5.68 S Completed

Recursive kavramı ile kullanırken kesinlikle “logging()” fonksiyonu(parametresiz) gereklidir. Aksi halde derleme zamanı hatası oluşacaktır. Bunu daha iyi anlayabilmemiz için,derleyicinin(Compiler) template şablonumuzu nasıl ele aldığını incelememiz gerekir.
Derleyicimiz “logging” fonksiyonunu aşağıdaki şekilde-sırayla ele alacaktır.

NOT: Template kullanırken,derleyici arka planda parametrelerin veri tipine uygun fonksiyon üretir.
Detay için C++ Variadic Templates makalemi inceleyin.

Yukarıda verilen fonksiyon çağrılarının karmaşık asm kodu ile boğuşmamanız için,daha basite indirgenmiş bir görseli bu linkte inceleyebilirsiniz.
Link

Son adım,yani 7.adımda ilgili fonksiyonumuzun parametresiz hali çağrıldığı görülüyor. Dolayısıyla bu fonksiyonu bizim sağlamamız bekleniyor.
Ancak bana göre bu durum C++ template özelliğinin tasarımsal bir eksiğidir. Dolayısıyla derleyicinin bur noktada insiyatif almasını beklerdim.
Neyse,Cpp tasarımcılarının muhtemelen “Fold kullan hacı” diye böyle bırakmışlardır. Sonra ben :”ulean hacı template ana özelliği,yani C++17 öncesi için dediydim” derim ve kavga çıkar.

Yukarıdaki örneği aşağıdaki şekilde değiştirelim ve sonucu inceleyelim.
Örnek1_2:

Output:
10 20 30 item 5.68 last:S

Çıktıyı incelediğimizde “logging()” parametresiz fonksiyonunu çağırmadığını görüyoruz. Template fonksiyonlarının çağrılış sırasını incelediğimizde aşağıdaki fa rkı görürüz.
İlk örneğe ait 6 = logging(‘S’); çağırdıktan sonra ilgili fonksiyonun parametresiz halini yani “7 = logging();” adımı gerçekleştiriyor.

Şimdi buna karşılık ikinci örneğimizin çağrılış sırasına bakalım.

Görüldüğü gibi en son parametremiz olan ‘S’ (char) direkt olarak “cout << “ operatörüyle ekrana basılmış ve süreç sonlandırılmıştır.
Ancak diğer çağrılış sırasındaki fonksiyonların içeriklerine baktığımızda,her fonksiyon “cout<<” operatörüyle ekrana ilgili veriyi bastıktan sonra bir sonraki fonksiyonu çağırıyor. Dolayısıyla bu örnekte “7 = logging();” adımı bulunmuyor. Ancak bunun yerine template ile tek parametreli fonksiyon tanımladık.
Recursive kavramı ile Variadic kullanıyorsak her halükarda zorunlu kaldığımız bir nokta ortaya çıkıyor.

Peki “Fold” kavramı ile durum nasıl olurdu? Yukarıda verilen örneği Fold ile inceleyelim.
Örnek2:

Fold ile Variadic kullandığımızda,sadece Variadic fonksiyonu yeterli olduğunu görüyoruz.Dolayısıyla recursive kavramıyla karşılaştırdığımızda,Fold kavramı daha anlaşılır ve az miktarda kod yazmamızı sağlıyor. Fold kavramı developer taraflı yapısal farklılık gösteriyor. Bunun yanı sıra,ayrıca arka planda üretilen kod-fonksiyonlar tarafında da olduğunu görebiliriz.
Üretilen fonksiyonları incelemek için Link.

İki kavramın sahip olduğu syntax’a yakından bakarsak,aslında aralarında çok fark olmadığını göreceğiz.
Örnek:

logging(args…); //recursive
(logging(args),…); //Fold

Yukarıdaki örneği biraz değiştirelim.
Örnek2_2:

Bu kullanımda Fold syntax’ı farklı bir kullanım olarak verilmiştir. Örnekte fonkisyon parametreleri ekrana direkt olarak “cout” kullanılarak basılmıştır.
Bu durumda ilk örneğe göre derleyicinin üreteceği fonksiyon kodlarında yapısal farklılık olacaktır.
Eğer kod çıktılarını karşılaştırır isek,bu kullanımda tüm parametrelerin tek fonksiyon altında “cout << “ operatörüyle çoğaltıldığını göreceğiz.
Ayrıca template ve fonksiyon da tek parametreye inmiştir.

İncelemek için Link

Farklı örneklerle incelemeye devam edelim.Aşağıdaki ilk örnekte Variadic recursive kullanılmıştır. Dolayısıyla yine aynı mantıkla son parametre için bir fonksiyon yazmamız gerekiyor.
Örnek3:

Output:
isum:35
ksum typeid:d
ksum:106.1

Yukarıdaki örnekte parametre olarak farklı veri
türlerinden(double,int,uint,double) veriler girilmiştir. Ancak çıktıyı incelediğimizde veri türünün double olduğunu görüyoruz. Veri türü sırasını değiştirip örneği incelerseniz,aslında ilk parametrenin türünü baz aldığını göreceksiniz.

Yukarıdaki örneği Fold ile yazalım.
Örnek3_2:

Output:
isum:35
ksum typeid:d
ksum:120.2

Yukarıdaki Fold kavramı örneğinde yine tek fonksiyonla tanımlanmıştır. Ayrıca çıktıyı incelediğimizde veri türünün double olduğunu görüyoruz. Ancak bu kez ilk örnekte olduğu gibi fonksiyonun ilk parametresi dikkate alınmadığını,ancak herhangi bir sırada geçen double türünün baz alındığını görüyoruz.
Parametre sırası ve türlerini değiştirip derleyici kod çıktılarını ve program çıktısını inceleyiniz.

Son olarak Fold ile bir vector içine değerler girilen bir örnek aşağıda verilmiştir.

Variadic Tricks

Variadic özelliğini kullanırken dikkat etmemiz gereken en önemli noktalardan ilki template parametreleridir. Aşağıdaki iki ayrı fonksiyon verilmiştir.
Bunlar aynı işi ve sonucu üretmelerine rağmen farklı parametrelere sahiptirler.

Output:
logging1 t:10 logging1 sizeof:5
20 30 item 5.68 S
— — — — — — — — — — — — —
logging2 sizeof:6
10 20 30 item 5.68 S

NOT:
Variadic parametrenin boyutunu almak için “sizeof…” komutu kullanılır.
Örnek: size_t size = sizeof…(Args);

Verilen örneği incelediğimizde “logging1” fonksiyonunun Variadic parametresi yanısıra ayrıca bir parametreye(T) sahiptir.
Program çıktısını incelediğimizde,bu fonksiyona parametre olarak gönderilen ilk(“t:10”)’i bu parametre üzerinden aktığığını görüyoruz.
Ve aslında 6 adet parametre göndermemize rağmen “logging1 sizeof:5” şeklinde bir çıktı veriyor.

Üretilen fonksiyonları incelemek için Link

Bir diğer husus ise ilgili template’da Variadic parametresinden sonra normal bir template parametresinin yer alamamasıdır.
Örnek:
template<typename T,typename M,typename… Args,typename S> //ERROR!

Ancak Variadic parametreler peş peşe tanımlanabilir.
Örnek:
template<typename T,typename M,typename… Args,typename… Args2> //Ok

Peki yukarıdaki gibi bir kullanımda işleyiş nasıl olur? Aşağıdaki örneği inceleyelim.

Output:
t typeid:i,val:10
m typeid:c,val:A
Args sizeof:5
B C 1 true string
— — — — — — — — — — — — — — — -
t typeid:i,val:10
m typeid:c,val:A
Args sizeof:0
Args2 sizeof:5
B C 1 true string

Sonuç

Yukarıda verilen örnekte görüldüğü gibi,fonksiyon parametrelerinin akışı template parametrelerine göre değişkenlik gösteriyor.
Özellikle “vtrick2” yi incelediğimizde,template yapısında bulunan son Variadic parametresi dikkate alındığını,önceki parametrenin boş olduğunu görüyoruz.
Variadic kullanılan bir template’da parametrelerinin akışı,sırası ve dolayısıyla önemi ortadadır. Böyle bir durumda “sizeof…” değerini herhangi bir kontrol veya farklı bir girdi olarak kullanmamız gerekirse,bu durumda Variadic işleyişini iyi bilmemiz ve buna göre önlem almamız gerekir.
Aksi halde istenmeyen sonuçlar doğacaktır.

Variadic özelliği özellikle Fold ile daha kullanışlı ve dolayısıyla temiz,anlaşılır kodlar yazmamıza olanak tanıyor. Fold kavramını daha detaylı inceleyeceğiz ancak bu makalenin daha fazla uzamaması için daha fazla örnek geliştirmeden kesmek zorunda kaldım.

Variadic özelliği ve dolayısıyla “Variadic Tricks” başlığı altında temeli makro’lara dayanan oldukça fazla örnekler işleyebilirdik.
Belki sadece “Variadic Tricks” üzerine kapsamlı bir makale yazabilirim ve/veya makalelerimde fırsat buldukça araya sıkıştırarak yer vermeye çalışacağım.

Bu makalede derleyici tarafında üretilen kodlar üzerinde pek durmadım. Ancak gerek derleyici gerek konuyla ilişkili sorularınız olursa cevaplamaya ve/veya sorulara ilişkin genişletilmiş makaleler yazmaya hazırım.

KAYNAKLAR:
https://en.cppreference.com/w/c/variadic
https://en.cppreference.com/w/cpp/language/fold
https://kerimfirat.medium.com/c-variadic-templates-50d23fb3f18b
https://cppinsights.io/

-end of

--

--

Kerim Fırat

Senior C++,Android Platform(AOSP,AAOS) Developer,Performance Architect,Open Source Contributor | Turkey Java User Group Vice Chairman | Author