Cpp Temel İlkeleri – enum türleri ve enum sabitleri

C++ Temel İlkeleri (CppCoreGuideLines) endüstride giderek daha fazla kabul görüyor. Bu ilkelere göre statik kod analizi yapan programların ya da eklentilerin sayısı giderek artıyor. Kişisel olarak ben de (birkaç istisna dışında) bu ilkeleri tamamen destekliyorum. Bu yazıda github‘da yayımlanan C++ Temel İlkelerinden enum türlerinin ve sabitlerinin kullanımına ilişkin maddelerin Türkçe çevirileri benim açıklamalarımla birlikte yer alıyor.

Enum.1: Sembolik sabitleri (makroları) kullanmak yerine enum türlerini kullanmayı tercih edin.

Önişlemci programı tarafından ele alınan makrolar kapsam (scope) kurallarına uymazlar ve bir tür kavramına sahip değillerdir. Önişlemci program tarafından kaynak koddan çıkartılan makrolar, hata ayıklayıcı programlar (debugger) ya da benzer araçlar tarafından (tipik olarak) görülmezler. Ayrıca bir makronun farklı değerlere tanımlanması yanlıştır. Sembolik sabit olarak kullanılan makrolarla ilgili yapılan kodlama hatalarının derleyici ya da kontrol araçları tarafından bulunması, saptanması çok zor hatta çoğu zaman olanaksızdır. Oysa enum türleri ve bunlara bağlı olarak enum sabitleri (enumaration constants / enumarators) derleyici tarafından derleme zamanında ele alınır. Derleyiciler ve statik kod analizi yapan programlar kaynak kodda kullanılan enum değerleri için birçok faydalı kontrolü gerçekleştirebilirler. enum türlerinin kullanımı makroların kullanımına göre her zaman daha güvenlidir. Muhtemelen eskiden yazılmış aşağıdaki koda bakalım:

Oysa enum class türlerinin kullanılması bu tür hataları önlediği gibi kodun okunmasını ve bakımını kolaylaştırır:

Bu ilkenin özeti şu: enum türlerini kullanabileceğiniz yerlerde asla sembolik sabitleri kullanmayın. Tercihiniz her zaman enum sınıfları olsun. Eğer bir tür kavramından bağımsız olarak isimlendirilmiş tek bir sabite gereksinim duyuyorsanız sembolik sabitler ya da enum sabitleri yerine her zaman constexpr değişkenleri kullanın. C++ dilini yeterince tanımayan C programcıları, C dilinde  edindikleri makro kullanma alışkanlıklarını C++ kodlarına da taşıyabiliyorlar. Bundan kesinlikle uzak durmak gerekiyor.
C++ Temel Kodlama İlkelerine göre, kod analizi yapan modern araçların kaynak kodda makro kullanıldığını saptamaları durumunda bir uyarıda bulunmaları öngörülüyor.

Enum.2: Birbiriyle ilişkili isimlendirilmiş sabitlerin temsili için enum türlerini kullanın:

Orijinal metinde bu ilkenin iyi bir şekilde iyi bir şekilde açıklandığını söyleyemeyeceğim. enum sabitleri (enumarators) birbirleriyle ilişkili olmalı ve bir tür olarak temsil edilebilmeli. (fontlar, pencere renkleri, haftanın günleri, aylar vs.)
Örnek:

enum türünün içerdiği sabitlerin case değerleri olarak kullanıldığı switch deyimleri sıklıkla yazılıyor. Aşağıdaki koda bakalım:

Bu tür switch deyimlerinde enum değerlerinden birinin switch deyiminde kullanılmaması yapılan tipik kodlama hatalarından biri. Bu hata tipik olarak enum türüne daha sonra yeni sabitlerin (enumaratörlerin) eklenmesi durumunda oluşuyor.

Statik kod analizi yapacak programlardan beklentiler:
Eğer bir switch deyimi enum türüne ilişkin sabitlerin çoğınu içeriyor ama bazılarını içermiyor ise uyarı mesajı verilsin.
Eğer bir switch deyiminde bir enum türüne ilişkin enum değerlerinden bazıları kullanılmış ancak default case kullanılmamış ise uyarı verilsin.

Enum.3: Geleneksel enum türleri yerine enum sınıflarını kullanmayı tercih edin

Geleneksel enum türlerinin kullanımında söz konusu olan eksiklikleri ve dezavantajları gidermeye yönelik olarak C++11 standartları ile dile enum sınıfları eklendi.
i) enum sınıflarının kendi kapsamları (scope) var. Böylece farklı enum class türlerine ilişkin enum sabitleri birbiriyle çakışmıyor.
ii) enum sınıfları için baz tür (underlying type) belirtilebiliyor. Böylece enum sınıfları için ön bildirim (forward declaration) yapılabiliyor. (Bu özellik geleneksel enum türlerine de eklendi)
iii) enum class türlerinden aritmetik türlerine otomatik tür dönüşümü (implicit type conversion) yapılmıyor. (Geleneksel enum türlerinden aritmetik türlere otomatik tür dönüşümünün yapılması kodlama hatalarına yol açabiliyor.)

Örnek:

Bunun yerine bir enum class kullanılması daha uygun olurdu:

Statik kod analizi yapacak programlardan beklentiler:
Geleneksel enum türlerinin kullanılması durumunda uyarı mesajı verilsin.

Enum.4: Güvenli ve kolay kullanım için enum sınıfları için operatör işlevleri yazın:

enum türlerinden değişkenlerin özellikle ++ ve — işleçlerinin operandı olabilmesi sık karşılaşılan bir ihtiyaç. Birçok programcı enum türleri için de operatör işlevlerinin yazılacağını bilmiyor. Oysa enum türleri için tanımlanan operatör işlevleri kullanıcı kodlara büyük kolaylıklar sağlayabiliyor. Örnek:

Yukarıdaki kodda yer alan global operator++ işlevi içinde static_cast tür dönüştürme işlecinin kullanımına dikkat edin. Tür dönüştürme işleci kullanılmasaydı özyinelemeli (recursive) bir çağrı yapılmış olurdu.

Statik kod analizi yapacak programlardan beklentiler:
Tür dönüştürme işleci ile enum türlerine dönüşüm yapılması durumunda uyarı mesajı verilsin.

Enum.5: Tamamı büyük harfler ile oluşturulmuş (all caps) enum sabitleri kullanmayın

Böyle bir isimlendirme sonucunda enum sabitleri ile makrolar arasında isim çakışması oluşabilir.
Kötü kullanım örneği:

Statik kod analizi yapacak programlardan beklentiler:
Tamamı büyük harf olan  (ALL_CAPS)  enum sabitlerinin kullanılması durumunda uyarı verilsin.

Enum.6: İsimlendirilmemiş enum türlerinden kaçının:

Eğer bir enum türü için uygun bir isin seçemiyorsanız enum sabitleri birbirleriyle ilişkili değildir. enum türünün kötü kullanımına bir örnek:

Yukarıdaki enum türüne bir isim verilmemiş. Çünkü verilebilecek uygun bir isim yok. Sözde bir enum türü oluşturulmuş olmasına karşın, isimlendirilmiş sabitlerin birbirleriyle hiçbir ilgisi olmadığı görülüyor. Burada enum türü kullanmak yerine constexpr değişkenler kullanmak çok daha iyi olurdu:

Enum.7: Gerekmedikçe bir enum türüne ilişkin baz türü (underlying type) belirtmeyin:

Varsayılan baz türü kullanmak kodun okunmasını ve yazılmasını kolaylaştırır. Varsayılan baz tür “int” tir. Ayrıca varsayılan int türünün kullanılması C dili ile uyumu da sağlar. C dilinde enum türlerinin baz türü int olmak zorundadır.

Örnek:

Not: Bir enum türünün ön bildiriminin (forward declaration) yapılması durumunda baz türün belirtilmesi gerekebilir:

Enum.8: Yalnızca gerekli olan durumlarda enumaratör değerlerini belirtin.

enum sabitlerinin varsayılan değerleri kolay kullanım olanağı sağlar. Varsayılan değerlerin kullanılmasıyla, bir enumaratör değerinin birden fazla isimle temsil edilmesi biçimindeki kodlama hatalarından kaçınılır. Varsayılan enum sabiti değerleri (0, 1, 2, …) switch deyimleri için daha etkin bir kod üretilmesine yardımcı olduğu gibi, statik kod analizi yapan programlara daha fazla kontrol olanağı verir.

Yukarıdaki kodda enum türü olan Col1‘de varsayılan enum sabiteri kullanılmış.
Col2 türünün bildiriminde yellow ve blue enumaratörleri aynı tamsayı sabitine işaret ediyor. (Yanlışlıkla yazılmış olmalı)
Month türünün bildiriminde enum sabitlerinin, ayların sıra numarasıyla eşleme sağlamak için, jan enum sabiti 1 olarak alınmış. (feb = 2, mar = 3 …). Böyle bir kalıplaşmış kullanmda bir yanlışlık ya da bir sorun söz konusu değil.

Statik kod analizi yapacak programlardan beklentiler:
Birden fazla enum sabiti aynı değerde ise uyarı verilsin.
Birbirini izleyen birer artan enum sabitleriyle (sabitler belirtilerek) oluşturulan enum türleri için uyarı mesajı verilsin.

Share

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Kod Eklemek İçin Okuyun