Evet sevgili yazılımperver dostlarım, kısa bir aradan sonra, bir süredir yazılımlarımda kullandığım ve sizlerin de kullanmasını şiddetle tavsiye ettiğim bir kütüphaneye derinlemesine göz atıyor olacağız: {fmt} kütüphanesi. Yazı uzun, işlecek kabiliyet çok o zaman çok oyalanmadan başlayalım.
İçerik
FMT nedir? Geliştiricisinin ağzıyla bakacak olursak:
{fmt} C stdio ve C++ iostream kabiliyetlerine, hızlı ve emniyetli bir alternatif sunan açık kaynaklı bir formatlama kütüphanesidir.
FMT kütüphanesi Victo Zverovich tarafından geliştirilen bir kütüphanedir. Bu kütüphaneyi de kapsayacak kabiliyetler, C++ 20 ile birlikte, yeni formatlama kütüphanesi olarak geliştiricilere de sunuldu. Kütüphanenin günce reposuna aşağıdaki adresten ulaşabilirsiniz:
Detaylara girmeden önce isterseniz {fmt}’ye ilişkin temel kabiliyetleri öncelikle bir sıralayalım:
-
Python benzeri modern formatlama yaklaşımı,
-
Yüksek performans (https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html),
-
Açık kaynaklı bir projedir,
-
Geniş karakter ve yerelleştirme desteği,
-
stdio ve iostream’e göre tip emniyeti ve derleme zamanında da format kontrolü sunma,
-
Kullanıcı tanımlı tipler ile kullanım,
-
Yüksek taşınabilirlik. Güncel sürüm C++ 11’in temel bir takım kabiliyetlerine ihtiyaç duysa da, C++ 98 için 4.x sürümü de halen desteklenmektedir,
-
C++ 20 formatlama kabiliyetlerini, C++ 11 ile sunma,
-
Kütüphane ve başlık olarak projelere eklenebilmektedir.
Temel Kullanım
Bu başlık altında {fmt} kütüphanesine ilişkin temel kullanıma bakıyor olacağız. Bunu da yaparken örnekler üzerinden gideceğiz. İlerleyen satırlarda, formatlama ve daha detaylı hususlara da değinececeğim.
Kütüphane ile sunulan temel başlık dosyaları ile başlayalım.
“fmt/core.h”, başlık dosyası ile temel formatlama, derleme zamanı kontrolleri ve minimum bağımlılık ile sunulmaktadır,
“fmt/format.h”, başlık dosyası ile bütün formatlama ve yerelleşitirme kabiliyetleri sunulmaktadır,
“fmt/ostream.h”, “fmt/printf.h”, başlık dosyaları ile printf/std::ostream desteği ve ilgili API’ler sunulmaktadır.
Bunları sadece ekleyerek kullanabilmek için (“header only” kullanım için), bu başlık dosyalarından önce, “#define FMT_HEADER_ONLY 1” tanımını yapmanız gerekiyor.
FMT kütüphanesi API’leri ve ilgili tipler, fmt adres uzayı içerisinde sunulmaktadır. Bu kütüphane ile sunulan temel API’lere aşağıdaki tablodan ulaşabilirsiniz:
Formatlama API’leri:
API İsmi | Çıktı/Dönüş | Açıklama |
---|---|---|
fmt::format() | std::string | Formatlama çıktısını std::string olarak döner. |
fmt::format_to() | fmt::memory_buffer / fmt::wmemory_buffer | Bir önceki API’den farklı olarak std::string nesnesi oluşturmadan, bellekte yer alana bir alana çıktıyı yerleştirir. |
fmt::sprintf() | std::string | sprintf API’si kullanımına benzer şekilde formatlama yaparak std::string döner. Her ne kadar, bu sprintf formatlaması kullanılsa da, aynı şekilde tip emniyeti sunulmakta ve hatalı kullanılmakta exception atılır. |
Çıktı API’leri:
API İsmi | Çıktı/Dönüş | Açıklama |
---|---|---|
fmt::print() | stdout / dosya(FILE*) | Formatlama çıktısını standard output’a ya da dosyaya basar. |
fmt::printf() | stdout | Formatlama çıktısını printf API’si kullanımına benzer şekilde formatlama yaparak standard output’a basar. Her ne kadar, bu printf formatlaması kullanılsa da, aynı şekilde tip emniyeti sunulmakta ve hatalı kullanılmakta exception atılır. |
fmt::fprintf() | Dosya (FILE*) / stream (std::basic_ostream) | Bir önceki API’nin dosya sistemine çıktıyı basan halidir. |
Bu API’ler temel olarak iki argüman almaktadırlar: fmt ve args.
fmt, gösterilecek/formatlanacak olan metin ve bu metin içerisinde kullanılacak olan değiştirilebilir alanlardan oluşan ({} içerisinde) stringdir. args, ise formatlanarak {} ile belirtilen alanlara geçirilecek nesnelerdir. Şimdi bu API’lerden birinin (fmt::format) tanımına bakalım:
1 2 |
template<typename ...T> auto fmt::format(format_string<T...> fmt, T&&... args) -> std::string |
Bu noktadan sonra, fmt::print’i kullanıyor olacağım, bunun yerine aynı şekilde fmt::format da kullanabilirsiniz.
Parantez Yer Değiştirme Alanları: Çok fazla oyalanmadan hemen kullanıma geçebiliriz. Öncelikle, formatlamak için verdiğiniz metinler içerisindeki formatlanacak alanlar {} ile ifade edilir:
1 2 |
// Çıktı: "Simdi buraya basacagimiz sayi: 42." fmt::print("Simdi buraya basacagimiz sayi: {}.", 42); |
Hemen aklınıza şu gelebilir. Sevgili yazılımperver, ben metin içerisinde { veya } kullanmak istersem ne yapacağım? Şunu yapacaksınız, önüne aynında bir dane daha ekleyeceğiz. Ör. {{ ya da }} gibi 😉
Parantez kullanımının bir diğer güzelliği de, özel formatlama yapmadığınız müddetçe, temel tipler için cout kullanımına benzer bir şekilde kullanabilirsiniz. Hemen bakalım
1 2 3 4 5 6 7 8 |
printf("%d", my_int); printf("%lld", my_long_long); printf("%" PRIu64, my_int64); // Yukarıdakiler yerine, aşağıdakilerini kullanabilirsiniz. Her biri için özel bir formatlama girdisine ihtiyacınız olmaz fmt::format("{}", my_int); fmt::format("{}", my_long_long); fmt::format("{}", my_int64); |
Konumsal Argümanlar: Bildiğiniz üzere printf ve iostream ile bir argümanı birden fazla kullanma veya argüman olarak verdiğiniz sıradan farklı bir şekilde kullanma şansına sahip değildiniz. Bu her ne kadar basit formatlama kullanımlarında sıkıntı olmasa da, karmaşık ya da tekrarlı kullanımlarda hakikaten yorucu olabiliyor. fmt bu anlamda güçlü. Hemen örnek üzerinden gidelim:
1 2 |
fmt::print("İmdi bana ilk degil de ikinci argumani ver {1} sonra ilkini ver {0}. Sonra bir daha ikiniciyi {1} ver.", "ilkArg", 12); // Çıktı: "Smdi bana ilk degil de ikinci argumani ver 12 sonra ilkini ver ilkArg. Sonra bir daha ikiniciyi 12 ver." |
İsimlendirilmiş Argümanlar: Ben sayılar, sıralar ile uğraşamam, argümanları isimlendirmek istiyorum derseniz. O da mevcut, fakat bunun bir maliyeti olacağını da unutmayın. Benzer şekilde, isimlendirilmiş argümanları da birden fazla yerde kullanabilirsiniz. Bu amaçlar fmt::arg(argumanIsmi, argumanDegeri) fonksiyonunu kullanabilirsiniz. Hemen örneğe bakalım.
1 2 3 4 |
fmt::display("[{id}] {lastName}, {firstName}", fmt::arg("id", 42), fmt::arg("firstName", "John"), fmt::arg("lastName", "Doe")); // Çıktı: [42] Doe, John |
Formatlama: Artık biraz formatlamanın detaylarına girebiliriz, bunun için de sunulan kabiliyetleri içerisinde barındıran ve geliştiricisinin verdiği bir örnek ile başlayalım. Aşağıdaki kod ile aslında formatlama namına, bu kütüphane ile neler yapabileceklerinizi (ya da günlük geliştirme faaliyetlerinizde) topluca görebilirsiniz. Formatlama tanımlaması, : noktadan sonra yapılıyor.
1 |
fmt::print("{:*^10.2f}", 1.2345); |
Ne der yukarıdaki kullanım?
Şunu der: 1.2345 kayan sayısını, sabit noktalı gösterim (presentation), noktadan sonra 2 rakam çözünürlükle yuvarla (precision) ve 10 karakter genişlik (width) içerisinde merkeze hizala (alignment) ve kalan boşlukları da * ile doldur (fill).
Peki çıktısı nedir: “***1.23***”
Evet, zehri verdikten sonra şimdi, formatlama kullanımına biraz daha eğilebiliriz.
fmt için verdiğiniz metinler içerisindeki, yer değiştirme alanları dışındaki kısımlar aynen çıktıya yansıtılır. Parantez içerisindeki kısım : ile ayrılıyor. Bunlar argüman tanımlayıcısı ve formatlama spesifikasyonudur:
{argüman tanımlayıcısı : formatlama spesifikasyonu}
Parantez içerisindeki iki kısım da boş bırakılabilir. İki nokta sadece formatlama spesifikasyonu olduğu durumda eklenmelidir. Şimdi birkaç örmeğe bakalım.
Geçerli kullanımlar:
-
{}, bir sonraki argümanı, tipine göre kullanır,
-
, bir sonraki argümanı, tipine göre kullanır (: biraz fazladan),
-
{0}, ilk argümanı, tipine göre kullanır,
-
{argumanIsmi}, argumanIsmi isimli argümanı, tipine göre kullanır,
-
{argumanNumarasi}, argumanNumarasi sırasındaki argümanı, tipine göre kullanır,
-
{:*^10.2f}, sonraki argümanı, yukarıda belirttiğim şekilde formatlar,
-
{3:*^10.2f}, 4. argümanı, yukarıda belirttiğim şekilde formatlar.
Geçersiz kullanımlar:
-
{#:}, iki noktadan önce ya numara, ya argüman ismi ya da hiç bir şey olmalı,
-
{*^10.2f}, formatlamanın yapılabilmesi için iki nokta kullanılmalı.
Şimdi ortaya karışık bir kaç örneğe bakalım:
1 2 3 4 5 6 7 8 9 10 |
fmt::print("0X{:X}", 42); // "0X2A" basar, 16'lik gosterim fmt::print("{:#X}", 42); // "0X2A" basar, 16'lik gosterim fmt::print("{0:5}", 42); // " 42" basar, sabit genişlik fmt::print("{2:{3}}", "gereksizArguman", "bu da oyle", 42, 7); // " 42" basar, dinamik (baska bır arguman kullanarak) genişlik fmt::print("{0:.3}", 1.234); // "1.23" basar, çözünrülük fmt::print("{2:.{3}}", "gereksizArguman", "bu da oyle", 1.234, 3); // "1.23" basar, dinamik (baska bir arguman kullanarak) çözünrülük fmt::print("{:<10}", "left"); // "left " basar fmt::print("{:>10}", "right"); // " right" basar fmt::print("{:^10}", "centered"); // " centered " basar fmt::print("{:-^14}", "centered"); // "---centered---" basar |
Mevcut printf formatlamalarının uyarlanması: Diyelim ki elimizde hazır printf kullanımları var, bunları hızlıca fmt kütüphanesine nasıl aktarabiliriz? Aslında çok zor değil, %’i silip : koymanız yeterli. Elbette etrafına {} koymayı unutmuyoruz. Hemen bir örneğe bakalım:
1 |
printf("%05.2f", 1.234); // 01.23 basar fmt::print("{:05.2f}", 1.234); // 01.23 basar |
Dosyaya/Standart Hata Alanına Çıktı: Komut satırına bastık, peki dosyaya da basabilir miyiz? Elbette hemen bir kaç örneğe bakalım:
1 |
fmt::print(stderr/FILE*, "System error code = {}\n", errno); // standart hata dosya tanımlayıcısına basar |
Belleğe Çıktı: Şimdiye kadar hep komut satırı ve std::string olarak formatlanmış çıktıları elde edebileceğimiz söylemiştik. Bunun yanında, bazı durumlarda, formatlanmış verileri bellekte de tutmak isteyebilirsiniz. Özellikle kısıtlı bellek kullanımlarında ya da çıktıların farklı yerler ile paylaşılması ihtiyaçları için fmt kütüphanesi buna ilişkin de API’ler sunmaktadır. Hemen bir örnek ile bu kabiliyete göz atıyor olalım.
1 2 3 |
fmt::memory_buffer buf; fmt::format_to(buf, "[{0}] {2}, {1}", 42, "John", "Doe"); // buf içerisine ilgili çıktıyı yerleştiriyor std::cout << buf.data() << '\n'; |
Yukarıdaki kullanımda, dinamik bellek kullanımına ihtiyaç bulunmuyor ve bellek boyutunu belirtmenize de ihtiyaç kalmıyor.
Formatlama Grameri
İlgili olanlarınız için formatlama gramerine de elbette bakacağız. Aslına bakarsanız, sizlere tavsiyem, grameri anlamaya biraz vakit ayırmanız. Bu sayede neler yapabileceğinize ilişkin ufkunuz oldukça genişleyecektir. Öncelikle yer değiştirme alanını (replacement_field) tanımlayalım:
1 2 3 4 5 6 7 |
replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}" arg_id ::= integer | identifier integer ::= digit+ digit ::= "0"..."9" identifier ::= id_start id_continue* id_start ::= "a"..."z" | "A"..."Z" | "_" id_continue ::= id_start | digit |
Sonra da, formatlama spesifikasyonuna bakabiliriz:
1 2 3 4 5 6 7 8 |
format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type] fill ::= <a character other than '{' or '}'> align ::= "<" | ">" | "^" sign ::= "+" | "-" | " " width ::= integer | "{" [arg_id] "}" precision ::= integer | "{" [arg_id] "}" type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "o" | "p" | "s" | "x" | "X" |
Hizalama opsiyonlarına bakalım:
Opsiyon | Anlamı |
---|---|
'<' |
Sola hizalar, çoğu nesne için varsayılan hizalama budur. |
'>' |
Sağa hizalar, sayılar için varsayılan hizalama budur. |
'^' |
Merkeze hizalar. |
İşaret (sign) opsiyonlarına bakalım:
Opsiyon | Anlamı |
---|---|
'+' |
Hem pozitif hem negatif sayılar için işaretleri kullan. |
'-' |
Sadece negatif sayılar için işareti kullan. Sayılar için varsayılan davranış budur. |
boşluk ‘ ‘ | Pozitif sayılar için ön taraf boşluk, negatif sayılar için ise işaret. |
Şimdi tip opsiyonlarına bakalım. Metin gösterim tipleri:
Tip | Anlam |
---|---|
's' |
String formatı. Bütün metinler için varsayılan formattır. |
Tip Tanımlayıcı Yok İse | 's' ile aynı. |
Karakter tip gösterim tipleri:
Tip | Anlam |
---|---|
'c' |
Karakter formatı. Karakter veri yapıları için varsayılan formattır. |
Tip Tanımlayıcı Yok İse | 'c' ile aynı. |
Öncelikle tam sayılar:
Tip | Anlam |
---|---|
'b' |
Binary format. Sayıları 2’lik sayı sistemine göre formatlar, “#” ile birlikte kullanılırsa, sayının önüne “0b” ekler. |
'B' |
Binary format. Sayıları 2’lik sayı sistemine göre formatlar, “#” ile birlikte kullanılırsa, sayının önüne “0B” ekler. |
'c' |
Karakter olarak basar. |
'd' |
Tam sayı. Sayıyı 10’luk düzene göre formatlar. |
'o' |
8 li format. Sayıyı 8’lik sayı düzenine göre formatlar. |
'x' |
16’lık sayı formatı. Sayısı 16’lık formatta basar ve 9 dan büyük harfleri küçük basar, “#” ile birlikte kullanılırsa, sayının önüne “0x” ekler. |
'X' |
16’lık sayı formatı. Sayısı 16’lık formatta basar ve 9 dan büyük harfleri büyük basar, “#” ile birlikte kullanılırsa, sayının önüne “0X” ekler. |
Tip Tanımlayıcı Yok İse | 'd' ile aynı. |
Kayan sayılar için tip opsiyonları:
Tip | Anlam |
---|---|
'a' |
Onaltılık kayan nokta biçimi. Sayıyı 16 tabanında “0x” öneki ve 9’un üzerindeki rakamlar için küçük harflerle yazdırır. Üslü belirtmek için ‘p’ kullanır. |
'A' |
Önek için büyük harfler, 9’un üzerindeki rakamlar ve üssü belirtmek için kullanılması dışında ‘a’ ile aynıdır. |
'e' |
Üs gösterimi. Üssü belirtmek için ‘e’ harfini kullanarak sayıyı bilimsel gösterimde yazdırır. |
'E' |
Üs gösterimi. Ayırıcı karakter olarak büyük harfli bir ‘E’ kullanması dışında ‘e’ ile aynıdır. |
'f' |
Sabit noktalı sayı. |
'F' |
Sabit nokta. ‘f’ ile aynıdır, ancak nan’ı NAN’a ve inf’yi INF’ye dönüştürür. |
'g' |
Genel biçim. Belirli bir p >= 1 kesinliği için, bu, sayıyı p anlamlı basamağa yuvarlar ve ardından sonucu büyüklüğüne bağlı olarak ya sabit nokta biçiminde ya da bilimsel gösterimde biçimlendirir. 1. |
'G' |
Genel biçim. Sayı çok büyürse ‘E’ye geçmesi dışında ‘g’ ile aynıdır. Sonsuzluk ve NaN temsilleri de büyük harfle yazılmıştır. |
Tip Tanımlayıcı Yok İse | Varsayılan kesinliğin, belirli bir değeri temsil etmek için gerektiği kadar yüksek olması dışında, ‘g’ye benzer. |
İşaretçiler için tip opsiyonları:
Tip | Anlam |
---|---|
'p' |
İşaretçi biçimi. Bu, işaretçiler için varsayılan türdür ve atlanabilir. |
Tip Tanımlayıcı Yok İse | 'p' ile aynı. |
Kullanıcı Tiplerinin Formatlanması
fmt kütüphanesinin sunduğu bir diğer güzellik de, var olan sınıf/veri yapılarınızı da, basit bir formatlayıcı yardımı ile bu kütüphanede kullanılabilir hale getirebilirsiniz. Hemen bir örneğe bakalım. Öncelikle bir veri yapısı tanımlayalım:
1 2 3 4 5 6 |
struct Material { int Id; double Weight; std::string Name; }; |
Veri yapımızı tamamladık. Şimdi, bu veri yapımızı fmt kütüphanesinin anlamlandırabilmesi için gerekli kabiliyetleri tanımlayalım.
1 2 3 4 5 6 7 8 9 10 11 12 |
template<> struct fmt::formatter<Material> { template <typename ParseContext> constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template <typename FormatContext> auto format(Material const& material, FormatContext& ctx) { return fmt::format_to(ctx.out(), "Name: {1} (Id:{0}). Weight: {2:^6.3f} gr", material.Id, material.Name, material.Weight); } }; |
Evet gelelim finale. Bu veri yapısını nasıl görünteleyebiliriz:
1 2 3 4 |
Material gold { 102, 13.456789, "Gold" }; fmt::print("{}\n", gold); // Çıktısı: "Name: Gold (Id:102). Weight: 13.457 gr" |
Zaman Formatlama
Yukarıda bir çok farklı formatlama örneğin baktıktan sonra şimdi biraz da, tarih ve saat çıktılarını nasıl formatlayabileceğimize bakacağız.
1 2 3 4 5 6 |
std::time_t t = std::time(nullptr); fmt::print("Today is {:%Y-%m-%d}\n", *std::localtime(&t)); // Today is 2021-11-23 std::time_t t = std::time(nullptr); fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t)); //"The date is 2021-11-23." (with the current date): |
Renklendirme
fmt kütüphanesinin bir diğer güzel özelliği de, fmt/color.h başlık dosyasını ekleyerek ve basit bir güncelleme ile, print API’siyle komut satırına renkli, italik ve altı çizili metinler basabilmeniz. Hemen örneğe bakalım. Burada, şunu belirtmekte fayda var, italik ve bold gösterim için kullandığınız terminalin de bunu destekliyor olması lazım:
1 2 3 4 5 6 7 8 9 10 |
#include <fmt/color.h> int main() { fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "Hello, {}!\n", "world"); fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | fmt::emphasis::underline, "Hello, {}!\n", "world"); fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, "Hello, {}!\n", "world"); } |
Emniyet
1 2 |
int x = 42; printf("%2s\n", x); |
Kod satırını derleyip çalıştırdığınızda ne olur? Çoğu modern derleyici bu tarz bir kullanım gördüğünde sizleri derleme zamanında uyarır. Eğer uyarmıyorsa -Wformat veya muadili ayarı kontrol etmenizde fayda var. Sonuç olarak çoğu durumda yazılımınızın istemsiz bir şekilde kapandığını göreceksiniz.
1 |
warning C4313: 'printf': '%s' in format string conflicts with argument 1 of type 'int' |
ya da
1 |
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘char *’ [-Wformat=] |
Bunun yanında, fmt çalışma zamanında bazı kontroller yapar. Olası hatalarda da, “exception” fırlatılır.
Hatalı argüman indeksi girişi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
try { fmt::print("[{}] {}, {}\n", 42, "Doe"); // üçüncü {} sıkıntılı } catch (fmt::format_error const& e) { fmt::print("{}\n", e.what()); // "argument not found" hatası } try { fmt::print("[{3}] {}\n", 42, "Doe"); // {3} mevcut değil } catch (fmt::format_error const& e) { fmt::print("{}\n", e.what()); // "argument not found" hatası } |
Hatalı tip tanımlayıcısı:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
try { fmt::print("Cevap sayi olmali {:d}", "kırk iki"); } catch (fmt::format_error const& e) { fmt::print("{}\n", e.what()); // "invalid type specifier" hatası } try { fmt::print("Cevap metin olmali {:s}", 42); } catch (fmt::format_error const& e) { fmt::print("{}\n", e.what()); // "invalid type specifier" hatası } |
Başta da bahsettiğim gibi, fmt kütüphanesi derleme zamanında da bazı kontroller yapıyor. Bunlar genel formatın doğruluğunu derleme zamanında, çeşitli hatalar ile size sunar. Burada, main.cpp (119) ile aslında hatalı kullanımın olduğu satır gösterilmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
fmt::print(FMT_STRING("Cevap sayi olmaliydi {:d}"), "kirk iki"); // Yukarıdaki satır asagidaki hatayı size derleme zamanında verir \fmt-master\include\fmt\core.h(2728,37): error C2131: expression did not evaluate to a constant \fmt-master\include\fmt\core.h(2576,31): message : failure was caused by call of undefined function or one not declared 'constexpr' \fmt-master\include\fmt\core.h(2576,31): message : see usage of 'fmt::v8::detail::error_handler::on_error' \fmt-master\include\fmt\core.h(2848): message : see reference to function template instantiation 'void fmt::v8::detail::check_format_string<const char(&)[9],S,0>(S)' being compiled with [ S=main::<lambda_25da53a982e860e05e523c13e643a462>::()::FMT_COMPILE_STRING ] \main.cpp(119): message : see reference to function template instantiation 'fmt::v8::basic_format_string<char,const char (&)[9]>::basic_format_string<main::<lambda_25da53a982e860e05e523c13e643a462>::()::FMT_COMPILE_STRING,0>(const S &)' being compiled > with > [ > S=main::<lambda_25da53a982e860e05e523c13e643a462>::()::FMT_COMPILE_STRING > ] \main.cpp(119): message : see reference to function template instantiation 'fmt::v8::basic_format_string<char,const char (&)[9]>::basic_format_string<main::<lambda_25da53a982e860e05e523c13e643a462>::()::FMT_COMPILE_STRING,0>(const S &)' being compiled > with > [ > S=main::<lambda_25da53a982e860e05e523c13e643a462>::()::FMT_COMPILE_STRING > ] |
Taşınabilirlik
C++ 11’in temel bir takım kabiliyetleri kullanması sebebi ile oldukça taşınabilir (GCC 4.8, CLang 3.4 ve VS 2015 üzeri derleyiciler ile kullanılabilmektedir). Bunun ile birlikte C++ 98 ve daha eski derleyiciler için 4.x sürümü de halen desteklenmektedir.
Bunun yanında normalde printf ile platform bağımlı çıktılar üretilen aşağıdaki gibi kullanımlarda, bir çok platform için aynı çıktıyı verir.
1 |
fmt::print("{}", std::numeric_limits<double>::infinity()); |
ki bu “inf”‘dir.
Performans
Kütüphane | Metot | Sn Cinsinden Çalışma Zamanı |
---|---|---|
libc | printf | 1.04 |
libc++ | std::ostream | 3.05 |
{fmt} 6.1.1 | fmt::print | 0.75 |
Boost Format 1.67 | boost::format | 7.24 |
Folly Format | folly::format | 2.23 |
{fmt}, yukarıda karşılaştırılan diğer yöntemlere göre en hızlısıdır, printf’den ~%35 daha hızlıdır.
Yukarıdaki sonuçlar, clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT ile macOS 10.14.6’da tinyformat_test.cpp oluşturularak ve üç çalıştırmadan en iyisi alınarak oluşturulmuştur (yazarın yalancısıyım). Testte, "%0.10f:%04d:%+g:%s:%p:%c:%%\n"
biçim dizesi veya eşdeğeri /dev/null’a gönderilen çıktıyla 2.000.000 kez doldurulur; daha fazla ayrıntı için bkz. .
{fmt} is the fastest of the benchmarked methods, ~35% faster than printf
.
The above results were generated by building tinyformat_test.cpp
on macOS 10.14.6 with clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT
, and taking the best of three runs. In the test, the format string "%0.10f:%04d:%+g:%s:%p:%c:%%\n"
or equivalent is filled 2,000,000 times with output sent to /dev/null
; for further details refer to the .
{fmt}, kayan nokta biçimlendirmesinde std::ostringstream ve sprintf’den 20-30 kata kadar daha hızlıdır (), ve
Diğer Dillerdeki Formatlama Kütüphaneleri İle Karşılaştırma
Öncelikle temel kullanımlara bir göz atalım:
C++:
1 2 |
auto text = fmt::format("[{}] {}, {}!", 2021, "Merhaba", "Dunya"); std::cout << text << '\n'; |
C#:
1 2 |
var text = string.Format("[{0}] {2}, {1}!", 2021, "Merhaba", "Dunya"); Console.WriteLine(text); |
Python:
1 2 |
<span role="presentation"><span class="cm-variable">auto</span> <span class="cm-variable">text</span> = <span class="cm-variable">fmt</span>::<span class="cm-builtin">format</span>(<span class="cm-string">"[{}] {}, {}!"</span>, <span class="cm-number">2021</span>, <span class="cm-string">"Merhaba"</span>, <span class="cm-string">"Dunya"</span>);</span> <span role="presentation"><span class="cm-variable">std</span>::<span class="cm-variable">cout</span> <span class="cm-operator"><<</span> <span class="cm-variable">text</span> <span class="cm-operator"><<</span> <span class="cm-string">'\n'</span>;</span> |
Çıktı:
1 |
<span role="presentation">[2021] Merhaba, Dunya!</span> |
Şimdi de isimlendirilmiş argüman kullanımlarına bir göz atalım:
C++:
1 2 3 4 |
<span role="presentation"><span class="cm-keyword">auto</span> <span class="cm-variable">text</span> <span class="cm-operator">=</span> <span class="cm-variable">fmt::format</span>(<span class="cm-string">"[{yil}] {ilkArg}, {ikinciArg}!"</span>,</span> <span role="presentation"> <span class="cm-variable">fmt::arg</span>(<span class="cm-string">"yil"</span>, <span class="cm-number">2021</span>), <span class="cm-variable">fmt::arg</span>(<span class="cm-string">"ilkArg"</span>, <span class="cm-string">"Merhaba"</span>),</span> <span role="presentation"> <span class="cm-variable">fmt::arg</span>(<span class="cm-string">"ikinciArg"</span>, <span class="cm-string">"Dunya"</span>));</span> <span role="presentation"><span class="cm-variable">std::cout</span> <span class="cm-operator"><<</span> <span class="cm-variable">text</span> <span class="cm-operator"><<</span> <span class="cm-string">'\n'</span>;</span> |
C#:
1 2 3 4 |
<span role="presentation"><span class="cm-variable">yil</span> <span class="cm-operator">=</span> <span class="cm-number">2021</span>;</span> <span role="presentation"><span class="cm-variable-3">string</span> <span class="cm-variable">ilkArg</span><span class="cm-operator">=</span><span class="cm-string">"Merhaba"</span>, <span class="cm-variable">ikinciArg</span><span class="cm-operator">=</span><span class="cm-string">"Dunya"</span>;</span> <span role="presentation"><span class="cm-keyword">var</span> <span class="cm-def">text</span> <span class="cm-operator">=</span> <span class="cm-variable">$</span><span class="cm-string">"[{yil}] {ilkArg}, {ikinciArg}!"</span>;</span> <span role="presentation"><span class="cm-variable">Console</span>.<span class="cm-variable">WriteLine</span>(<span class="cm-variable">text</span>);</span> |
Python:
1 2 3 |
<span role="presentation"><span class="cm-variable">text</span> = <span class="cm-string">"[{yil}] {ilkArg}, {ikinciArg}"</span>.<span class="cm-property">format</span>(<span class="cm-variable">yil</span>=<span class="cm-number">2021</span>, <span class="cm-variable">ilkArg</span>=<span class="cm-string">"ilkArg"</span>,</span> <span role="presentation"><span class="cm-variable">ikinciArg</span>=<span class="cm-string">"ikinciArg"</span>)</span> <span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">text</span>)</span> |
Çıktı:
1 2 |
<span role="presentation">[2021] Merhaba, Dünya! </span> |