SOLID yazılarımızı da bitirdiğimize göre artık biraz C++ a dönüş yapabiliriz. Bu yazımda C++ 20 standardı için önerilen kabiliyetler arasında kendine yer edinmiş, bir kaç yeni kabiliyetten bahsedeceğim. Kısa ve öz bir yazı olacak 🙂
İçerik
Uzay gemisi operatörü (“<=>”)
İlk bahsedeceğim kabiliyet, kimi kaynaklarda “uzay gemisi operatörü” diye adlandırılan ‘<=>’ operatörü. Bunun standart operatör ismi, “three-way comparison operator“, yani üç yollu karşılaştırma operatörü. Bu kabiliyet C++ 20 ile birlikte sunulmakta ve yazılması gereken kod miktarını ciddi bir biçimde azaltacak bir kabiliyet. Bu kabiliyet şu anda VS 2019 (16.2) tarafından da destekleniyor.
Bu operatör bize ne sağlar? Temel olarak bu operatör, derleyicinin, temel sıralama (“lexicographical”) ile ilgili kodları oluşturmasına olanak sağlar ve bu karşılaştırma operatörleri için herhangi bir fonksiyon tanımlanmasına ihtiyaç olmuyor. Hemen bir örnek üzerinden buna bakalım (referans sayfasından):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class Point { int mX; int mY; public: // Asagidaki satir ile derleyiciden kibarca bizim icin bu operatorleri tanimlamasini istiyoruz auto operator<=>(const Point&) const = default; // Diger metotlar ... }; // Derleyici tarafından bütün altı ilişkilendirme operatörü üretiliyor Point pt1, pt2; // Herhangi bir sıkıntı yok if (pt1 == pt2) { /*...*/ } // Herhangi bir sıkıntı yok std::set<Point> s; s.insert(pt1); // Herhangi bir sıkıntı yok if (pt1 <= pt2) { /*...*/ } |
Yeni operatör ile aşağıdaki satırı eklediğimiz zaman derleyici bizim için ==, !=, >, <, >=, <= operatörlerine sahip. Tahmin edebileceğiniz üzere, bu bizi oldukça fazla kodtan, bu basit örnek için bile kurtarıyor. Burada karşılaştırma, türetilen baz sınıfları soldan sağa ve statik olmayan üye değişkenleride tanımlama sırasına göre karşılaştırır.
Bu tarz karmaşık tiplerin karşılaştırılması için https://devblogs.microsoft.com/cppblog/simplify-your-code-with-rocket-science-c20s-spaceship-operator/ adresinden bir örnek ile devam edelim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// Isme cok takilmayin o kadar da basit bir yapi olmayacak struct Basics { int i; char c; float f; double d; auto operator<=>(const Basics&) const = default; }; struct Arrays { int ai[1]; char ac[2]; float af[3]; double ad[2][2]; auto operator<=>(const Arrays&) const = default; }; struct Bases : Basics, Arrays { auto operator<=>(const Bases&) const = default; }; int main() { constexpr Bases a = { { 0, 'c', 1.f, 1. }, { { 1 }, { 'a', 'b' }, { 1.f, 2.f, 3.f }, { { 1., 2. }, { 3., 4. } } } }; constexpr Bases b = { { 0, 'c', 1.f, 1. }, { { 1 }, { 'a', 'b' }, { 1.f, 2.f, 3.f }, { { 1., 2. }, { 3., 4. } } } }; static_assert(a == b); static_assert(!(a != b)); static_assert(!(a < b)); static_assert(a <= b); static_assert(!(a > b)); static_assert(a >= b); return 0; } |
Burada derleyici, her bir baz sınıfların veya üye değişkenlerin karşılaştırılması aktivitelerini otomatik olarak halletmektedir.
Burada tabi, akıllara bir soru gelebilir. Şöyle ki, varsayılan karşılaştırma mantığından farklı bir karşılaştırma ihtiyacı olursa ne olacak, ya da doğal karşılaştırma sıralamasından farklı bir ihtiyacınız olursa ne olacak? O zaman da bu operatörü siz tanımlayabilirsiniz. Burada da dönüş tipine göre farklı operatörleri tanımlayabilirsiniz. Bu konua https://en.cppreference.com/w/cpp/language/default_comparisons adresine başvurabilirsiniz.
Bu kabiliyeti, C++ 20 öncesi sürümlerde kullanılmak için ya da bu desteği sunmayan derleyiciler için Henrik Sjöström’ün yazısına (https://www.fluentcpp.com/2019/04/09/how-to-emulate-the-spaceship-operator-before-c20-with-crtp/) başvurabilirsiniz.
Bu kabiliyete ilişkin kısmı tamamlamadan önce bir konuya daha değinmek istiyorum. O da, C++ 20 ile birlikte, dil seviyesinde karşılaştırma mekanizması da değişmekte ve https://brevzin.github.io/c++/2019/07/28/comparisons-cpp20/ sayfasında bu konu oldukça detaylı ve güzel bir şekilde anlatıyor. Yukarıda verdiğim referans da, bu konuda bilgi içermekte ama sakin bir kafa ile bu yazıları okumanızı öneriyorum 🙂 Ayruca bir yazımı da, bu konuya ayırabilirim. Yalnız burada, verdiğim kaynaktan bir kaç hususa değinmek istiyorum. Öncelikle, bu üç yönlü karşılaştırma olayı aslında C programlama dili kütüphaneleri aracılığı ile sunulan memcmp/strcmp() API’leri ile sağlanıyordu. Şöyleki, ilk parameter, ikinciden büyük ise pozitif, küçük ise negatif ve aynı ise 0 dönülmekteydi. Peki bu uzay gemisi operatöründe ne dönülüyor? Bu durumda üç kategoriden birisi dönülmekte:
strong_ordering: Bu sıralama kategörisinde eşitlik, birbirleri yerine kullanılabilirlik anlamına da gelmekte. Yani “(a <=> b) == strong_ordering::equal ” demek, fonksiyonlar için f(a) ve f(b) sonuçlarının aynı olacağı anlamına gelir. Burada alınabilecek değerler strong_ordering::greater, strong_ordering::equal, ve strong_ordering::less.
weak_ordering: Bu sıralama kategorisinde ise eşitlik, bir denklik sınıfı oluşturmakta. Ne demek abi bu? Hemen bir örnek üzerinden açıklamaya çalışalım. Elimizde iki adet karakter dizisi olduğunu farz edelim, bunları karakter büyüklüğünden bağımsız olarak aynı olsa bile, bu da göz önüne alındığında aynı olmadığı durumlardır. Bu durumda alınan değer weak_ordering::equivalent (equal değil dikkat).
partial_ordering: Bu sıralama kategorisinde ise yukarıda bahsettiğimiz, büyük, küçük, eşit ya da denk durumlar yanında, sıralı olmayan “unordered” olarak nitelendirilen bir grup vardır. İşte bu kategori de bunları kapsar. Ör. 1.f <=> NaN karşılaştırması partial_ordering::unordered olmakta (bu arada bilmeyenler için NaN, “Not a Number”, yani sayı değil anlamına gelmektedir).
Yukaridaki gruplara baktığınız zaman, strong_ordering her zaman için öncelikli tercih olmalıdır. Bu kategorilere ilişkin örneklere de https://en.cppreference.com/w/cpp/language/default_comparisons sayfasından ulaşabilirsiniz.
std::source_location
C++ 20 öncesinde C++ kodu içerisinden, ilgili satır sayısı ve benzeri bilgilere erişmek için __LINE__ ve __FILE__ makro tanımlamaları kullanılmaktaydı. C++ 20 ile birlikte artık, source_location sınıfı sunulmakta ve bunu ilk bahsettiğim konu amacı ile kullanılabilir. Bu kabiliyeti kullanmak için <source_location> başlık dosyası eklenmesi gerekir.
Sınıfın direk bir nesnesini oluşturabileceğiniz gibi current() API’sini kullanarak dönülen source_location nesnesi de bu anlamda kullanılabilir. Örneğin aşağıdaki kullanıma ilişkin örnekte, log metodunun çağrıldığı nokta ile ilgili bilgiler veriliyor olacak. Bu arada bu kabiliyet de oldukça yeni ve gördüğüm kadarı ile bir çok derleyici bunu sunmuyor. Sunanlar da experimental isim uzayı altında sunuyor olabilirler, onun için buna dikkat ediniz lütfen ve derleyicinizin dokümanına başvurabilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> #include <string> #include <source_location> //#include <experimental/source_location> void log(const& std::string message, const std::source_location& location = std::source_location::current()) { std::cout << "Log Info:" << location.file_name() << ":" << location.line() << " " << message << '\n'; } int main() { std::source_location mainInstance; std::cout << "Log Info <main>:" << mainInstance.file_name() << ":" << mainInstance.function_name() << ":" << mainInstance.line() << " " << mainInstance.column() << '\n'; log("Hello world!"); return 0; } |
Şablon (template) parametresi olarak string kullanımı
C++ 20 öncesinde, C++ şablonlarının parametrelerinde karakter dizisi ya da std::string kullanamıyorduk. C++ 20 ile birlikte artık bu mümkün olacak. Burada hedeflenen, std::basic_fixed_string ile sunulan constexpr yapıcısı (derleme zamanında kullanıma olanak sağlar) kullanılarak, bu karakter dizisinin, derleme zamanında oluşturulmasına dayanmaktadır. Hemen bir örnek ile inceleyelim:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
template<std::basic_fixed_string T> class Foo { static constexpr char const* Name = T; public: void hello() const; }; int main() { Foo<"Hello!"> foo; foo.hello(); } |
Bu kabiliyete ilişkin mevzuyu kapatmadan önce, gerçekten mevcut C++ derleyicileri ile bu kabiliyet sağlanamıyor mu diye baktığımda, C++ 11 ile birlikte pek de güzel olmasa da, bir şekilde bu karakter dizilerinin de kullanılabildiğini gördüm. Bu konuyu merak edenler aşağıdaki sorudaki tartışmaya bir göz atabilirler:
Evet arkadaşlar, bir yazımızın daha sonuna geldim. Burada C++ 20 de gelecek (ki şubat 2020 de gelebilir) bazı kabiliyetlere şimdiden bir göz attık. Tabi bunların yanında çok daha baba bazı kabiliyetler var (Concepts, Ranges, Coroutines, Modules) ama henüz onlara ben de vakıf değilim 🙂 Onları da inceledikten sonra sizler ile paylaşacağım.
O zamana kadar bol yazılımlı günler.
Kaynaklar
- https://www.youtube.com/watch?v=8jNXy3K2Wpk
- https://blog.tartanllama.xyz/spaceship-operator/
- https://www.fluentcpp.com/2019/04/09/how-to-emulate-the-spaceship-operator-before-c20-with-crtp/
- https://devblogs.microsoft.com/cppblog/simplify-your-code-with-rocket-science-c20s-spaceship-operator/
- https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison
- https://en.cppreference.com/w/cpp/language/default_comparisons
- https://brevzin.github.io/c++/2019/07/28/comparisons-cpp20/
- http://www.open-std.org/jtc1/SC22/wg21/docs/papers/2018/p0732r2.pdf
- https://stackoverflow.com/questions/2033110/passing-a-string-literal-as-a-parameter-to-a-c-template-class