1 puan yazan GN⁺ 2024-10-19 | 1 yorum | WhatsApp'ta paylaş
  • P3477R0, C++’ta bayt boyutunu CHAR_BIT adlı uygulama tanımlı bir değere bırakmak yerine tam olarak 8 bit olarak sabitlemeyi öneren bir standart değişikliği teklifidir
  • Modern platformlar zaten 8 bit bayt varsayımında birleşmiş durumda; GCC, LLVM ve MSVC de ilgili varsayılan değerleri veya makroları 8 olarak belirliyor
  • POSIX, POSIX.1-2001’den beri CHAR_BIT == 8 şartını arıyor; C++20 ve C23’teki iki’nin tümleyeni tamsayı gösterimi benimseme eğilimi de aynı yönde
  • 8 bit olmayan bayt desteğinin dil, kütüphane ve toolchain genelinde küçük istisnalar bıraktığı; modern C++ kullanımıyla örtüşmeyen uç durum yükü oluşturduğu düşünülüyor
  • PDP-10 veya bazı DSP’ler gibi istisnai mimariler mevcut olsa da, yeni C++ standardının bu hedefler için karmaşıklığı sürdürmeye devam edip etmemesi temel tartışma noktası

P3477R0’ın değişiklik hedefi

  • C++, C’deki CHAR_BIT makrosunu devralır; mevcut durumda bu değer, bir bayttaki bit sayısını ifade eden uygulama tanımlı bir değerdir
  • P3477R0, C++ standardının baytı resmen 8 bit olarak şart koşacak şekilde değiştirilmesini öneriyor
  • Bilgisayarların ilk dönemlerinde farklı bayt boyutlarına izin veren esnekliğin anlamlı olduğu, ancak modern donanımın neredeyse tamamen 8 bit bayt varsayımında birleştiği değerlendirmesi temel alınıyor

Derleyicilerin ve platformların mevcut durumu

  • Başlıca derleyiciler 8 bit baytı zaten varsayılan gerçeklik olarak ele alıyor
    • GCC, varsayılan değeri 8 olarak tutuyor ve upstream hedefler arasında bu varsayılanı değiştiren bir örnek yok
    • LLVM, __CHAR_BIT__ değerini 8 olarak ayarlıyor
    • MSVC, CHAR_BIT değerini 8 olarak tanımlıyor
  • GCC’nin geçmiş destek örneklerinden dsp16xx 2004’te, 1750a ise 2002’de kaldırıldı
  • Web aramalarında BITS_PER_UNIT değeri 8 olmayan bazı GCC dışı portlar görülse de bunların modern C++ ile ilgili görünmediği değerlendiriliyor

POSIX ve tamsayı gösterimindeki yönelim

  • POSIX, POSIX.1-2001’den beri şu koşulları şart koşuyor
    • Bayt tam olarak 8 bittir
    • CHAR_BIT, 8dir
    • SCHAR_MAX 127, SCHAR_MIN -128, UCHAR_MAX ise 255tir
  • POSIX, int8_t eklenmesinin sonucu olarak 8 bit char ve iki’nin tümleyeni aritmetiğini gerektirdiğini açıklıyor
  • C++20, P0907r4 sonrasında yalnızca iki’nin tümleyeni depolama biçimini destekliyor; C23 de aynı yönü izliyor
  • POSIX uyumlu güncel işletim sistemi örnekleri olarak AIX, HP-UX, INTEGRITY, macOS, OpenServer, UnixWare, VxWorks ve vz/OS listeleniyor

8 bit olmayan baytların bıraktığı maliyet

  • 8 bit bayt için yazılmış yazılım ile 8 bit olmayan bayt için yazılmış yazılım birbiriyle uyumlu değildir; 8 bit olmayan bayt hedefli C/C++ kodunun fiilen C ve C++’ın uyumsuz lehçelerine yakın olduğu düşünülüyor
  • 8 bit olmayan bayt mimarilerini desteklemek, dilin ve kütüphanenin birçok bölümünde küçük ama gereksiz bir karmaşıklık bırakıyor
  • Derleyiciler ve toolchain’ler, modern kullanımı yansıtmayan uç durumları taşımaya devam etmek zorunda kalıyor
  • Yeni programcıların C++’ın bu egzotik özellikleri nedeniyle kolayca kafa karışıklığı yaşaması olası
  • Bazı deneyimli programcıların da var olmayan platformlar için “taşınabilirlik” üzerine zaman harcadığı değerlendiriliyor

İstisnai mimariler ve uzlaşma seçenekleri

  • Öneri, 8 bit olmayan bayt işlemcilerinin hâlâ var olduğunu kabul ediyor
  • Temel soru, bu işlemcilerin modern C++ ile ilgili olup olmadığı ve bu işlemcilerin kullanıcılarının yeni C++ sürümlerini kullanıp kullanmayacağı
  • Uzlaşma olarak CHAR_BIT % 8 == 0 şartının aranması da öneriliyor; ancak bu yalnızca komitenin CHAR_BIT değeri 8 olmayan ama 8’in katı olan DSP’leri veya diğer işlemcileri desteklemeye devam etme kararı alması durumunda anlamlı
  • PDP-10 tartışma konusu olsa da PDP-11’in 8 bit bayt kullandığı ayrıca belirtiliyor
  • Bazı DSP’ler 24 bit veya 32 bit word’leri “bayt” gibi ele alıyor; bu tür mimariler, word boyutlarının çeşitli olduğu ve bayt kavramının standartlaşmadığı dönemlerde makuldü

Standart metnindeki değişiklik yönü

  • intro.memory içindeki bayt tanımını değiştirerek C++ bellek modelinin temel depolama birimi olan baytın 8 bit olduğunu açıkça belirtmek amaçlanıyor
  • climits içinde CHAR_BIT değerini 8 olarak belirleyen yönde metin değişikliği öneriliyor
  • cstdint içinde, bayt 8 bit olacağı için int8_t, uint8_t gibi genişliği belirtilmiş tamsayı tipleri ve ilgili makrolar artık isteğe bağlı olmayacak
  • _N_ kullanan tiplerde N değerinin 8, 16, 32, 64 olmadığı durumlar ise isteğe bağlı kalmaya devam edecek
  • localization içindeki CHAR_BIT == 8 ile ilgili 4 mandate hükmünün kaldırılmasını içeren bir değişiklik de yer alıyor

C standardıyla ilişkisi

  • Öneri, C++’ın 8 bit olmayan bayt mimarileriyle ilgili kalmaya devam edip etmemesi gerektiğini değerlendiriyor
  • C komitesi, C dili için farklı bir sonuca varabilir
  • İki komitenin hizalanması ideal olsa da bu öneri, WG14 ve SG22 irtibat grubunun WG21’e bilgi sağlaması yönünde konumlanıyor

1 yorum

 
GN⁺ 2024-10-19
Hacker News görüşleri
  • JF’nin “gerçek bilgisayarların zaten böyle çalıştığını kabul edemez miyiz?” serisinde daha önce işaretli tamsayılar 2’nin tümleyeni kullanır başlıklı bir bölüm vardı: "Signed Integers are Two’s Complement"

  • 1986’da stajyerken 10 bit bayt kullanan BBN C/70 üzerinde C kodu yazmıştım; korkunç bir deneyimdi ve böyle bir makinenin en başta var olmuş olması bile olumsuz anlamda kozmik bir kazaydı

    • DECSYSTEM-20 üzerinde de kod yazdım; C derleyicisi resmî olarak desteklenmiyordu
      36 bit word ve 7 bit bayt kullanıyordu; baytları bir word’e doldurunca artan bitler kalıyordu. Üstüne bir de 8 bit formatında ikili veri içeren bantları okuma görevi verilince tam bir felaket olmuştu
    • Intel Intellivision CPU’sunu programladım; 10 bit decl kullanan acayip bir makineydi ve C çalıştıracak kadar güçlü değildi
    • 9 bit bayt ve 81 bit komutlar kullanan bir makinede, ayrıca 6 bit bayt kullanan başka bir makinede çalıştım; ama ikisinde de C derleyicisi yoktu
    • Bugün FPGA dünyasında 10 bit aritmetik aslında pek de nadir değil ve görece modern ürünlerde bile kullanılıyor
      Ama konu 10 bit C olunca durum değişiyor
  • D, şu kararları vererek büyük bir ilerleme sağladı: bayt 8 bit, short 16 bit, int 32 bit, long 64 bit, aritmetik 2’nin tümleyeni, kayan nokta ise IEEE floating point
    Bunları soyutlamaya çalışıp sonunda yine hata yapmaya harcanan muazzam zaman tasarruf edildi ve milyonlarca insan rahat bir nefes aldı. Karakter kümesi de EBCDIC ya da RADIX-50 değil, Unicode oldu

    • Zig daha da iyi: u8/i8, u16/i16, u32/i32, u64/i64 gibi boyutlar açıkça belirtiliyor ve aritmetik de açık seçik seçiliyor
      + taşması hatalı davranış sayılır; debug ve releasesafe modlarında durur, +% 2’nin tümleyenine göre wrapping, +| ise doygun aritmetiktir. @addWithOverflow() özgün tür ile u1 içeren bir tuple verir, std.math.add() ise taşma durumunda hata döndürür. f16, f32, f64, f80, f128 de sırasıyla o bit uzunluklarındaki IEEE floating point türleridir. Baytın kaç bit olduğu önemli değildir; 12 bit bayt kullanan bir makinede u12 ve i12 kullanırsınız
    • D’nin büyük bir ilerleme sağladığını söylemek abartı. Açık boyut adları kullanan u8, i32 gibi tür adları her bakımdan çok daha iyi
    • “Bayt 8 bit” ise bitin boyutu ne kadar?
    • Bunu D dilinin yazarı Bright’ın bizzat söylemesi biraz kendini övme gibi değil mi? :)
    • Java da bu konuda doğruyu yaptı. unsigned tarafını kötü ele aldı ama temel türlerin bit sayısını standartlaştırma işini doğru yaptı
      byte = 8 bits, short = 16, int = 32, long = 64, float = 32 bit IEEE, double = 64 bit IEEE
  • Hâlâ DSP ile uğraşmak zorunda olan insanlar var: https://thephd.dev/conformance-should-mean-something-fputc-and-freestanding#we-cannot-program-on--vibes-
    Ben ise kişisel olarak, “rakibe göre bayt başına %50 daha fazla bit!” diye pazarlanan, hiç uygulanmamış bir 12 bit fantezi konsolu eğlencesine dokümantasyon yazıyorum; içine “UTF-12” gibi icatlar da koydum

    • Hâlâ hangi hedeflerin gerçekten ilgili olduğunu ve bunların modern C++’ı hedefleyip hedeflemediğini ya da hedefleme planları olup olmadığını doğrulamaya çalışıyorum
      Bunu yıllardır soruyorum ama olumlu bir yanıt alamadım; adı geçen tek örnek TI olduğu için güncel taslağa bilgi ekledim: https://isocpp.org/files/papers/D3477R1.html
    • Bence sadece C++23 ve öncesini hedeflemek yeterli olurdu. Elimde birkaç SHARC var ama komite C++30 gibi bir sürümde CHAR_BIT=32 desteğini kaldırsa buna ağlayarak itiraz edecek değilim
    • PDP-8 12 bit bayt kullanmıyor muydu?
  • C++'ın bir şeyi kullanımdan kaldırıp veya basitleştirip basitleştiremeyeceği merak ediliyor
    Dürüst bir soru; ayrıntıları yakından takip etmedim. rand()'ın bozuk olduğu ve düzeltilemeyeceği söyleniyordu; en son bildiğim kadarıyla hâlâ kullanımdan kaldırılma yolunda bile değildi. Bu öneri, “kelimenin tam anlamıyla kimsenin karşılaşmadığı bir soruna çözüm desteğini de kaldırabilir miyiz?” testi gibi görünüyor

    • İşaretli tamsayıların mutlaka 2'nin tümleyeni olması gerekmiyordu; işaret-büyüklük, 1'in tümleyeni ve 2'nin tümleyeni olmak üzere üç gösterim de geçerliydi
      Modern C ve C++ bunu bırakıp 2'nin tümleyenini zorunlu kıldı. Burada “as if” ayrımı da pratikte pek önemli değil ve aynı yaklaşım CHAR_BIT için de uygulanabilir; yani böyle bir değişimin emsali açıkça var
    • Trigraph'lar zaten kaldırıldı, rand de kullanımdan kaldırılacak olarak işaretlendi ve alternatifleri de sunuldu
      Bunun dışında p2809 Trivial infinite loops are not Undefined Behavior, p1152 Deprecating volatile, p0907 Signed Integers are Two's Complement, p2723 Zero-initialize objects of automatic storage duration, p2186 Removing Garbage Collection Support da var. Yani bir şeyleri değiştirmek mümkün
    • C++11'deki GC API C++23'te kaldırıldı; GC destekleyen başlıca türevler olan Unreal C++ ve C++/CLI'nin ihtiyaçları gözetilerek tasarlanmadığı düşünülürse bu anlaşılır
      İstisna belirtimleri de kaldırıldı, ama değer tipi istisnalar için bunları geri getirmek isteyenler de var. auto_ptr da bozuk tasarımı yüzünden kaldırıldı. Yine de basitleştirme açısından çok bir iyileşme yok; çünkü eski yöntemi hâlâ bilmek gerekiyor
    • Mükemmelliği bozmamak, onun yerine daha fazla mükemmellik biriktirmek gerektiğine dair bir hiciv gibi duruyor
      Yani uyumluluğu bozmadan 8 bit baytı güvenilir biçimde ifade eden yeni bir C++ simgesi gerekirmiş gibi. Mesela unsigned byte8, 2'nin tümleyeni signed byte8, işaret davranışı tanımlanmamış byte8 yapılabilir. Muhasebeciler için değer aralığı 0~10, -10~+10 ile sınırlı unsigned decimal byte8 ve signed decimal byte8, bayt maliyetini bile hesaplayan muhasebeciler için 0~100, -100~+100 aralıklı centimal byte8, veritabanındaki age alanı için aşağı yukarı yeterli bir tür ve tabii ki float byte8 da ekleyelim diye dalga geçiliyor
    • rand()'ın neden bozuk olduğunu bilmiyorum
      Rastgele görünen değerler üretiyor ve amaç da bu. Kriptografik olarak güvenli rastgele sayılar üretmemesi doğal; başka dillerdeki eşdeğer işlevler de aynı. Hızlı hesaplanan, yeterince rastgele tamsayılar gerekiyorsa rand() gayet yeterli çalışıyor
  • Öneriye ilgi gösterdiğiniz için teşekkürler; şimdiye kadar gelen geri bildirimlere dayanarak güncellenmiş bir taslak hazırladım: https://isocpp.org/files/papers/D3477R1.html

    • Öneri metninin alaycı üslubu hoşuma gitti
      Özellikle “Sorun, baytın 8 bit olmadığı mimarilerin hâlâ var olup olmaması değil. Varlar! Sorun, onların modern C++'ı önemseyip önemsemediği ve modern C++'ın onları önemseyip önemsemediğidir” cümlesi etkileyici
  • Bu öneriye karşı karışık duygularım var. Bir yandan açıkça doğru ve CHAR_BIT'in 8 olmamasının anlamlı bir kullanım alanı yok
    Öte yandan, dünyayı yalnızca bilgisayarların iç işleyişine dair kişisel ve aşırı basitleştirilmiş bir modelle mantıklı ve akıl yürütülebilir kılmak isteyen adil dünya yanılgısına teslim olmak gibi de hissettiriyor. Bu yaklaşım bir yere kadar çok işe yarıyor ama sonunda çıkmaz sokağa giriyor; en nihayetinde hiçbir şey bilmediğinizi ve doğru programı ancak belgeler doğruysa kurduğunuza dair biçimsel bir argümanın elinizdeki en iyi şey olduğunu kabul etmek gerekiyor. Bu büyük bir zihinsel sıçrama ve kişisel olarak buna ne kadar geç zorlandıysam sonradan aşması o kadar zor oldu. Yine de bugünlerde yeni başlayanlar arasında fiziksel elektronik projeleri popülerleşiyor gibi; bu yüzden umarım “belgeleri oku” yerine “lanet olası datasheet'i oku” yeni standart olur

    • Buna rağmen her autoconf betiği çalıştırdığımda bir bayttaki bit sayısını kontrol edip bunu config.h içine yazdığını görüyorum. Sanki biri gerçekten o değere göre davranmayı planlıyormuş gibi görünüyor
    • Yaygın kullanılan bir dil eninde sonunda COBOL problemiyle karşılaşır. Çoğu durumda sorun çıkmaz, ama güncellemenin zorlandığı bir sistemde bir anda trafik kontrol sistemi durabilir ya da bir uçak düşebilir
      Önceki kodun tamamını derleme sırasında tarayıp bu makronun zaten kullanılıp kullanılmadığını kontrol edecek bir yol gerekli. Böyle kırıcı değişiklikler dili parçalama riski de taşır. Mevcut kod tabanlarının CHAR_BIT makrosunu kullanıp kullanmadığını ve yeni bir derleyiciye güncellenip güncellenemeyeceğini test etmenin zorluğu da net değil. Hangi kütüphanelerin kırılmış sayılacağı veya CHAR_BIT kullanılarak derlenmiş başka kodlarla etkileşimde de sorun çıkıp çıkmayacağı gibi sorular da var. Sezgilere aykırı olduğuna katılıyorum, ama önce dönüştürme araçları üretip en uç durumlarda bile güvenli olduğunu gösterdikten sonra geçmek daha iyi olur
  • Tartışmaya kapalı olduğu hâlde inanılmaz derecede acı bir öneri; bu yüzden hoşuma gidiyor

  • int8_t == char == 8 bits zorunluluğu tamamen kabul edilebilir, ama baytın 8 bit olduğu yanılgısını yayma konusunda emin değilim
    8 bitlik bayta octet denir. Ayrıca C++17'den beri byte, zaten char için bir tür “takma ad” gibi: https://en.cppreference.com/w/cpp/types/byte

    • Bilgisayarla ilk tanışmam 45 yıl önceydi ve o zaman da “byte” 8 bitlik bir nicelik olarak tanımlanıyordu
      Sonraki 45 yıl boyunca “byte”ın başka bir anlamda kullanıldığını hiç görmedim; dolayısıyla 8 bit olmayan bir “byte” tanımı varsa bunun için kaynak görmek isterim
    • Ağ RFC'leri en başından beri sürekli octet terimini kullanıyor
    • Hayır, bayt 8 bittir
      Bu betimleyici bir ifade değil, kural koyucu bir ifade
    • Kişisel olarak int8 == signed char olmasından hoşlanmıyorum
      std::cout << (int8_t)32 << std::endl; doğal olarak 32 yazdırmalıdır
  • C++ ile ilgili değil ama, 6 bitlik bayt retro mikrobilgisayar fikri kulağıma oldukça hoş geliyor. Böylece 24 bit bir sözcük oluyor
    Mikrobilgisayarlar genelde az sayıda nesneyle çalışır ve pointer'lardan çok dizileri tercih eder, bu yüzden bellekten tasarruf edilebilir. VGA'de renk başına 6 bit vardı, 6x4 bitlik bir matrisle okunabilir alfabeler oluşturulabilir, temel LISP ya da Forth dilleri de 6 bitlik bir alfabeye sığdırılabilir ve orijinal System/360 da yalnızca 24 bit adres kullanıyordu. Bağımsız olarak adreslenebilen 6 bitlik birimlerden oluşan 12MiB bellek herkes için yeterli olmalı. Yetmezse FAT-12 doğal biçimde FAT-24'e genişletilebilir ya da 64 bit pointer'lar kadar kullanışlı 48 bit pointer'lar kullanılabilir

    • Ya da 8 bitlik bayt ve 3 baytlık sözcük kullanılabilir. O da yine 24 bit eder