CMake’te String’ler ve Listeler

Programlama dillerinde String’ler ve liste türlerinin (vektör vb.) ne derece önemli oladuğunu anlatmamıza gerek yoktur. Hemen hemen yazdığımız her programda karşımıza çıkarlar ve çeşitli şekillerde manipüle edilip ele alınırlar. String’ler ve listeler CMake konfigürasyon dosyalarında da önemli bir yere sahiptirler. Bu yazıda CMake’de yer alan String ve listeleri nasıl oluşturabileceğimizden ve onları nasıl ele alabileceğimizden bahsedeceğim. Önce String’lerden başlayalım.

String (Karakter Katarı) dediğimiz şeyin aslında karakterlerden meydana gelen bir yapı olduğunu biliyorsunuz. CMake’te bir değişkene String değer vermek oldukça basittir. Eğer String boşluk karakterlerini içermiyorsa, onu değer yerine (tırnak işaretleri olmadan da) doğrudan yazabilirsiniz:

set(test mustafa)
set(test2 yemural)
set(test3 mustafa_yemural)

Ancak String içerisinde boşluklar yer alıyorsa, o zaman onu çift tırnak arasına yazmanız gerekir. Çünkü bunu yapmazsanız CMake bu durumu beklediğinizden çok farklı bir biçimde yorumlayabilir (birazdan anlatacağım). Örnek:

set(test "test deger")
set(name "mustafa yemural")
set(description "this is a simple program")

Bir önceki yazıda gösterdiğim message komutu ile de String tutan değişkenlerin değerlerini ekrana kolayca yazdırabilirsiniz:

message("Name: ${name}, Author: ${description}")

Yukarıdan da anlaşılacağı gibi aslında message komutuna verdiğimiz argüman da bir String’dir. Bu da demek oluyor ki değişkenleri bu String’in içerisine bağladığımız gibi, başka String’ler tutan değişkenlere de aynı bağlamayı yapabilmemiz mümkündür. Örnek:

set(name "Mustafa Yemural")
set(desc "simple description")
set(msg "This is a message: ${name}, ${desc}")

message(${msg})
ÇIKTI
This is a message: Mustafa Yemural, simple description

Bu da aslında CMake’in String’ler konusunda ne kadar esnek olduğunu gösteren bir örnektir. Peki, bildiğiniz gibi programlama dillerinde String’leri manipüle edebileceğiniz onlarca fonksiyon bulunmaktadır. Bu fonksiyonlar ile String’ler içerisinde arama, sıralama ve değiştirme gibi işlemleri kolaylıkla yapabilirsiniz. Peki CMake’te bunları nasıl yapabiliriz ve yapabileceğimiz komutlar var mı? Aslında CMake’te yer alan string isimli komut tam da bu işler için kullanılmaktadır. Bu komutun genel formu şu şekildedir:

string(İŞLEM <parametreler>…)

Bu komutun ilk argümanı yapılacak olan işlemin anahtar kelimesini almaktadır. Diğer argümanlar ise bu argümana bağlı olarak değişmektedir. Şimdi yapılabilecek işlemlerden birkaçını göstererek durumu açıklayalım. Öncelikle bir String üzerinde arama yapmak ve sonucu geri döndürmek için FIND anahtar kelimesi kullanılmaktadır:

string(FIND <string> <substring> <output_variable> [REVERSE])

Bu komut ile “<string>” içerisinde “<substring>” araması yapılır ve sonuç “<ouput_variable>” ile belirtilen değişkene geri döndürülür. Eğer son argümana REVERSE anahtar kelimesi verilirse arama işlemi tersten gerçekleştirilir. Eğer arama sonucu bir şey bulunamazsa, çıktı değişkenine -1 değeri verilir. Arama sonucu başarılı olursa, “<string>” içerisinde “<substring>” String’inin bulunduğu ilk konum bilgisi çıktı değişkenine değer olarak verilir (burada ilk karakterin indisinin sıfırdan başladığına dikkat edin). Örnekler:

set(msg "This is the dummy message and ummy message is dummy.")
set(word dummy)

string(FIND ${msg} ${word} forwardOut)
string(FIND ${msg} ${word} backwardOut REVERSE)

message("Forward Index: ${forwardOut}, Backward Index: ${backwardOut}")
ÇIKTI
Forward Index: 12, Backward Index: 46

Bu örnekten farkedebileceğiniz gibi çıktı kısmına verilen değişken isminin önceden set ile tanımlanmış olmasına gerek yoktur. Olmayan bir isim verirseniz o anda tanımlama yapılacaktır. Şimdi de bir String’in içinde arama yapıp bulduklarımızı başka String’ler ile değiştirmeyi nasıl yapabileceğimize bakalım. Bu iş için REPLACE anahtar kelimesi kullanılmaktadır. Bu anahtar kelime ile kullanılan string fonksiyonunun prototipi şöyledir:

string(REPLACE <match_string> <replace_string> <output_variable> <input> [<input>…])

Bu şekilde “<input>” kısmına yazılan String veya String’ler içinde “<match_string>” String’i aranır ve bulunanlar “<replace_string>” ile değiştirilir. Sonuç olarak ortayan çıkan String “<output_variable>” değişkeninde kullanılır. Örnek:

string(REPLACE "test" "mustafa" output "This is the test String and test output.")
message("Output: ${output}")
ÇIKTI
Output: This is the mustafa String and mustafa output.

Burada eğer girdi olarak birden fazla String verilirse, bu durumda String’ler aralarında herhangi bir ayraç bulunmadan birbirine bağlanıp sonuç “<output_variable>” değişkenine aktarılır. Ancak bu genellikle karışık durumlara sebep olduğundan kullanımı pek tercih edilmez. Şimdi de bu komut ile düzenli ifadeleri (regular expressions) kullanarak nasıl arama ve değiştirme yapabileceğimize bakalım. Bunun için REGEX anahtar kelimesini kullanabiliriz. Bu anahtar kelime ile kullanımın 3 formu bulunmaktadır:

string(REGEX MATCH <regular_expression> <output_variable> <input> [<input>…])

Bu form tıpkı “FIND” kullanımına benzer, String’in içinde arama yapar ve bulunan ilk yerin konumunu “<output_variable>” değişkenine geri döndürür. Eğer String içindeki bulunan her bir konumu bir liste olarak almak isterseniz, MATCHALL formunu kullanabilirsiniz:

string(REGEX MATCHALL <regular_expression> <output_variable> <input> [<input>…])

Tıpkı REPLACE gibi düzenli ifadelerle yer değiştirme işlemi yapmak isterseniz, şu formu kullanabilirsiniz:

string(REGEX REPLACE <regular_expression> <replacement_expression> <output_variable> <input> [<input>…])

Her üç formda da eğer birden fazla “<input>” sağlarsanız, bu girdiler önce birbirleri ile bağlanıp daha sonra işlem yapılacaktır. “<output_variable>” mantığı zaten hepsinde FIND ve REPLACE işlemlerinkine benzerdir. Yalnızca MATCHALL fromunun bir liste döndürdüğüne dikkat edin. Düzenli ifadeler ile ilgili ayrıntılı bilgileri burada anlatmayacağım. Bunun için CMake dokümantasyonundaki şu kısmı inceleyebilirsiniz:

https://cmake.org/cmake/help/latest/command/string.html#regex-specification

Düzenli ifadeler ile ilgili birkaç örnek verip diğer işlemlere geçeceğim:

set(str test1test2test3)

string(REGEX MATCHALL "test*" outMatch ${str})
string(REGEX REPLACE "([0-9])" "x\\1x" outReplace ${str})

message("MATCHALL = ${outMatch}")
message("REPLACE = ${outReplace}")
ÇIKTI
MATCHALL = test;test;test
REPLACE = testx1xtestx2xtestx3x

CMake 3.4 ile gelen APPEND anahtar kelimesi ile bir String’i diğer String’in sonuna; CMake 3.10 ile gelen PREPEND anahtar kelimesiyle de bir String’i diğer String’in başına bağlamanız mümkündür:

string(APPEND <string_variable> [<input>…])

string(PREPEND <string_variable> [<input>…])

set(strAppend "Mustafa Yemural")
set(strPrepend "Mustafa Yemural")

string(APPEND strAppend ", mustafayemural.com" ", test")
string(PREPEND strPrepend "test, ")

message("APPEND = ${strAppend}")
message("PREPEND = ${strPrepend}")
ÇIKTI
APPEND = Mustafa Yemural, mustafayemural.com, test
PREPEND = test, Mustafa Yemural

String’leri birbirine bağlayıp sonucu bir değişkene atamak için CONCAT ve CMake 3.12 ile gelen JOIN anahtar kelimelerini kullanabilirsiniz. JOIN kullanımında yer alan “<glue>” değeri String’ler arasına yerleştirilecek ayraç karakterini (veya katakterlerini) belirtir:

string(CONCAT <output_variable> [<input>…])

string(JOIN <glue> <output_variable> [<input>…])

string(CONCAT outConcat "str1 " "str2 " "str3 ")
string(JOIN "," outJoin "str1" "str2" "str3")

message("CONCAT = ${outConcat}")
message("JOIN = ${outJoin}")
ÇIKTI
CONCAT = str1 str2 str3
JOIN = str1,str2,str3

Bir String’in tüm karakterlerini küçük harf yapmak için TOLOWER, büyük harf yapmak için TOUPPER anahtar kelimelerini kullanabilirsiniz. Yine bir String’in başında ve sonunda yer alan boşlukları silmek için STRIP anahtar kelimesini kullanabilirsiniz. Bir String’in karakter sayısını bir değişkene atamak için ise LENGTH anahtar kelimesini kullanabilirsiniz:

string(TOLOWER <string> <output_variable>)

string(TOUPPER <string> <output_variable>)

string(STRIP <string> <output_variable>)

string(LENGTH <string> <output_variable>)

Örnekler:

string(TOLOWER "Test String" outLower)
string(TOUPPER "Test String" outUpper)
string(LENGTH "Test String" len)
string(STRIP "  Test String    " outStrip)

message("TOLOWER = ${outLower}, TOUPPER = ${outUpper}")
message("LENGTH = ${len}, STRIP = ${outStrip}")
ÇIKTI
TOLOWER = test string, TOUPPER = TEST STRING
LENGTH = 11, STRIP = Test String

CMake 3.15 ile gelen REPEAT anahtar kelimesi ile bir String’i belli bir sayıda ard arda ekleyebilirsiniz:

string(REPEAT <string> <count> <output_variable>)

Örnek:

string(REPEAT "Test" 5 out)

message("REPEAT = ${out}")
ÇIKTI
REPEAT = TestTestTestTestTest

Son olarak da SUBSTRING anahtar kelimesinden bahsedelim. Bu anahtar kelime ile kullanımda, “<begin>” kısmına başlangıç indisi, “<length>” kısmına ise uzunluk bilgisi yazılarak bir String’in belli bir kısmı “<output_variable>” değişkenine geri döndürülebilir. Eğer “<length>” kısmına -1 değeri verilirse, bu durumda başlangıç konumundan String’in sonuna kadar olan kısım geri döner:

string(SUBSTRING <string> <begin> <length> <output_variable>)

Örnek:

string(SUBSTRING "Mustafa Yemural" 8 -1 out)

message("SUBSTRING = ${out}")
ÇIKTI
SUBSTRING = Yemural

Biraz da listelerden bahsedelim. Aslında CMake’te listeler birbirlerine noktalı virgül (;) ile bağlanmış String’lerden ibarettirler. Yani biz boşluklar içeren bir String ile (başta bahsettiğim gibi) bir değişkene değer verdiğimizde, CMake otomatik olarak bunu liste olarak algılayacaktır ve aralarına nokralı virgül yerleştirecektir. Yani şu 3 değişkenin değeri de birbirinin aynısı şekilde liste belirtmektedir:

set(test1 item1 item2 item3)
set(test2 item1;item2;item3)
set(test3 "item1;item2;item3")

Tıpkı String’lerde olduğu gibi listeler üzerinde de çeşitli işlemler yapabilirsiniz. Bunun için list komutunu kullanabilirsiniz. Bu komut ilk argümanına çeşitli anahtar kelimeler alır ve diğer argümanların yapısı buna göre şekillenir. Ancak genellikle ikinci argümanı ele alacağımız listenin kendisi olur. Bu argümana a;b;c şeklinde doğrudan değerler vermek mümkün değildir. Bu argümana verilecek olan listenin bir değişkene atanmış olması gerekmektedir. İlk olarak list komutunda LENGTH ve GET anahtar kelimelerinin kullanımlarını inceleyelim:

list(LENGTH <list> <output variable>)

list(GET <list> <element index> [<element index> …] <output variable>)

LENGTH ile bir listenin boyutunu bir değişkene kaydedebilirsiniz. GET ile de bir listedeki çeşitli elemanları indis numaralarına göre elde edip yeni bir liste oluşturabilirsiniz. Örnek:

set(testList a b c d e f g)

list(LENGTH testList outLen)
list(GET testList 1 3 5 outGet)

message("LENGTH: ${outLen}, GET: ${outGet}")
ÇIKTI
LENGTH: 7, GET: b;d;f

CMake 3.12 ile gelen JOIN ile liste elemanlarını ayraçlar ile bağlayıp bir String haline getirebilir; yine CMake 3.12 ile gelen SUBLIST ile bir başlangıç indisi ve boyut bilgisi ile bir listeden alt bir liste elde edebilirsiniz. Bu iki anahtar kelimenin formatları ve örneği şöyledir:

list(JOIN <list> <glue> <output variable>)

list(SUBLIST <list> <begin> <length> <output variable>)

set(testList a b c d e f g)

list(JOIN testList ":" outJoin)
list(SUBLIST testList 0 3 outSub)

message("JOIN: ${outJoin}, SUBLIST: ${outSub}")
ÇIKTI
JOIN: a:b:c:d:e:f:g, SUBLIST: a;b;c

Bunun dışında listenin sonuna çeşitli elemanlar eklemek istediğinizde APPEND anahtar kelimesini, listenin herhangi bir indisine bir eleman eklemek istediğinizde ise INSERT anahtar kelimesini kullanabilirsiniz. Bu iki anahtar kelimenin kullanımı ve örnekleri şöyledir:

list(APPEND <list> [<element> …])

list(INSERT <list> <element_index> <element> [<element> …])

set(testList a b c d e f g)

list(APPEND testList h i j)
list(INSERT testList 1 x y)

message("RESULT: ${testList}")
ÇIKTI
RESULT: a;x;y;b;c;d;e;f;g;h;i;j

Bu işlemleri yaparken girdiğiniz liste değişkeninin üzerinde işlem yapıldığına dikkat edin. Yine string komutundakine oldukça benzer olan FIND işlemi, list komutunda da yer almaktadır. Bunun sadece formatını vermem yeterli olacaktır, kullanımı string ile hemen hemen aynıdır:

list(FIND <list> <value> <output variable>)

Listeden eleman silmek istediğinizde genellikle REMOVE_ITEM veya REMOVE_AT işlemlerinden biri kullanılmaktadır. REMOVE_ITEM ile belli bir değer listeden silinebilirken, REMOVE_AT ile de belli bir indis numarasından silme yapılabilir. Bunun dışında listede birden fazla yer alan elemanların fazla olanları direk olarak silmek için REMOVE_DUPLICATES anahtar kelimesi kullanılır:

list(REMOVE_ITEM <list> <value> [<value> …])

list(REMOVE_AT <list> <index> [<index> …])

list(REMOVE_DUPLICATES <list>)

set(testList a b c d e f g)

list(REMOVE_AT testList 0)
list(REMOVE_ITEM testList f g)

message("RESULT: ${testList}")

set(dups a a a a b b b b c)

list(REMOVE_DUPLICATES dups)

message("Unique List: ${dups}")
ÇIKTI
RESULT: b;c;d;e
Unique List: a;b;c

CMake 3.15 ile listenin doğrudan önünden veya arkasından silme yapabileceğiniz POP_FRONT ve POP_BACK anahtar kelimeleri gelmiştir. Bu anahtar kelimeleri kullanarak listenin ön veya arka tarafından silme işlemi yapıp silinen değerleri değişkenlere atabilirsiniz. Eğer herhangi bir değişken belirilmemişse, listenin önünden veya arkasından sadece 1 eleman silinecektir. Aksi takdirde belirtilen değişken sayısı kadar silme işlemi gerçekleştirilecek ve elemanlar sırasıyla bu değişkenlere atılacaktır:

list(POP_BACK <list> [<out-var>…])

list(POP_FRONT <list> [<out-var>…])

set(testList a b c d e f g)

list(POP_FRONT testList) # a deleted
list(POP_FRONT testList test1 test2) # b, c deleted
list(POP_BACK testList test3) # g deleted

message("RESULT: ${testList}")
message("test1: ${test1}, test2: ${test2}, test3: ${test3}")
ÇIKTI
RESULT: d;e;f
test1: b, test2: c, test3: g

Son olarak da listeyi tersine çevirme ve sıralama işlemlerinden bahsedelim. Listeyi tersine çevirmek için REVERSE anahtar kelimesi, sıralamak için ise SORT anahtar kelimesi kullanılabilir. Bu işlemler doğrudan verilen liste üzerinde işlem yapmaktadırlar. SORT anahtar kelimesi ile kullanımda gördüğünüz ve yazılması seçeneksel olan argümanlar CMake 3.13 ile gelmiştir. Bu argümanlardan burada bahsetmeyeceğim.

list(REVERSE <list>)

list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])

set(list1 a b c d)
set(list2 g e d c a b c)

list(REVERSE list1)
list(SORT list2)

message("RESULT1: ${list1}")
message("RESULT2: ${list2}")
ÇIKTI
RESULT1: d;c;b;a
RESULT2: a;b;c;c;d;e;g

Bu yazıda CMake’te String ve listelerin ne olduğu ve onlarla ilgili işlemlerin nasıl yapılabileceğini anlattım. Burada sadece genel kullanımlarını gösterdim, çünkü genellikle String ve listeler kontrol ve döngü yapıları ile birlikte anlamlı kullanımlara sahiptirler. Sonraki yazılarda anlatacağım kontrol ve döngü yapıları ile burada gördüklerimizi pekiştirmeyi düşünüyorum.

5 5 votes
Article Rating
Subscribe
Bildir
guest

0 Yorum
Inline Feedbacks
View all comments