← Kursa Dön
📄 Text · 15 min

std::format (C++20) ile Modern String Formatlama

Bir restoranda sipariş verdiğini düşün. Garson sana "Ne istersiniz?" diye soruyor, sen de "Bir adet 150 gramlık orta pişmiş bonfile, yanında patates püresi, içecek olarak sade su" diyorsun. Garson bunu kısa bir notla yazdı: "1x bonfile(150g, med), patates, su". İşte string formatlama tam bu — verileri alıp belirli bir şablona göre okunabilir metne dönüştürmek. C++ bu işi yıllarca printf ve iostream ile yaptı — ikisi de farklı şekillerde sorunluydu. C++20 ile gelen std::format, Python'un f-string'lerine benzer modern, güvenli ve esnek bir çözüm sunuyor.


Eski Yöntemlerin Sorunları

printf: Güçlü Ama Tehlikeli

C'den miras kalan printf, format string'i ile veri tiplerini elle eşleştirmeni gerektirir. Eşleştirme yanlışsa derleyici (çoğu zaman) uyarmaz ve tanımsız davranış oluşur.

#include <cstdio>
#include <string>

void printfSorunlari() {
    int yas = 25;
    const char* isim = "Ali";

    // Doğru kullanım
    printf("Isim: %s, Yas: %d\n", isim, yas);

    // TEHLIKE 1: Yanlış format specifier — UB
    printf("Yas: %s\n", yas);    // %s ama int verdik — çökebilir!

    // TEHLIKE 2: Argüman sayısı uyumsuz
    printf("Isim: %s, Yas: %d, Sehir: %s\n", isim, yas);
    // 3 placeholder var, 2 argüman — çöp değer veya çökme

    // TEHLIKE 3: std::string doğrudan çalışmaz
    std::string sehir = "Istanbul";
    printf("Sehir: %s\n", sehir);        // YANLIS: UB
    printf("Sehir: %s\n", sehir.c_str()); // Dogru ama zahmetli
}

printf'in en büyük sorunu tip güvenliği olmamasıdır. Format string %d deyip double verirsen, derleyici bunu hata olarak görmez. Sonuç: çöp çıktı veya çökme.

iostream: Güvenli Ama Zahmetli

iostream tip güvenlidir ama karmaşık formatlama yapmak çok uzun ve okunaksız kod üretir.

#include <iostream>
#include <iomanip>
#include <string>

void iostreamSorunlari() {
    double fiyat = 1234.5678;
    int adet = 42;
    std::string urun = "Laptop";

    // Basit çıktı — idare eder
    std::cout << urun << ": " << adet << " adet" << std::endl;

    // Formatlı çıktı — kabus başlıyor
    std::cout << std::left << std::setw(15) << urun
              << std::right << std::setw(5) << adet << " adet, "
              << std::fixed << std::setprecision(2)
              << fiyat << " TL" << std::endl;

    // Hex formatlama
    std::cout << "Hex: " << std::hex << std::showbase
              << std::uppercase << 255 << std::endl;

    // Dikkat: state kalıcıdır!
    std::cout << 42 << std::endl;  // Hala hex modunda: "0X2A"

    // Resetlemek gerekiyor
    std::cout << std::dec << std::noshowbase
              << std::nouppercase;
}

İki büyük sorunu var. Birincisi: setw, setprecision, setfill gibi manipülatörlerle formatlama kodu, asıl veriyi gölgede bırakır. Neyin yazdırıldığını anlamak için kodu üç kez okuman gerekir. İkincisi: std::hex, std::fixed gibi bazı manipülatörler stream state'ini kalıcı olarak değiştirir. Resetlemeyi unutursan sonraki çıktılar bozulur.

std::to_string + Concatenation

#include <string>
#include <iostream>

void toStringProblemleri() {
    std::string isim = "Ali";
    int yas = 25;
    double maas = 15750.5;

    // Her şeyi string'e çevirip birleştir
    std::string mesaj = "Isim: " + isim + ", Yas: "
                        + std::to_string(yas) + ", Maas: "
                        + std::to_string(maas) + " TL";

    std::cout << mesaj << std::endl;
    // Çıktı: Isim: Ali, Yas: 25, Maas: 15750.500000 TL

    // Sorunlar:
    // 1. Hassasiyet kontrolü yok (15750.500000)
    // 2. Hex, oct formatlama yok
    // 3. Genişlik/hizalama yok
    // 4. Çok fazla + operatörü — okunabilirlik düşük
    // 5. Her + geçici string oluşturabilir (performans)
}

std::to_string basit dönüşümler için işe yarar ama format kontrolü yoktur. Ondalık hassasiyet, hizalama, dolgu gibi şeyler imkansızdır.


std::format Temelleri

İlk Adımlar

std::format, Python'un str.format() ve Rust'un format! makrosundan ilham alır. <format> başlık dosyasından gelir.

#include <format>
#include <iostream>
#include <string>

int main() {
    std::string isim = "Ali";
    int yas = 25;
    double maas = 15750.5;

    // Temel kullanım — {} yer tutucu (placeholder)
    std::string mesaj = std::format("Isim: {}, Yas: {}, Maas: {:.2f} TL",
                                     isim, yas, maas);
    std::cout << mesaj << std::endl;
    // Çıktı: Isim: Ali, Yas: 25, Maas: 15750.50 TL

    // Indeksli argümanlar — sırayı değiştir
    std::string ters = std::format("{1} yasinda {0}", isim, yas);
    std::cout << ters << std::endl;
    // Çıktı: 25 yasinda Ali

    // Aynı argümanı birden fazla kullan
    std::string tekrar = std::format("{0} {0} {0}", "hey");
    std::cout << tekrar << std::endl;
    // Çıktı: hey hey hey

    return 0;
}

Syntax basit: {} otomatik sıralı, {0}, {1} indeksli. İkisini karıştıramazsın — ya hepsi otomatik ya hepsi indeksli.

Derleme

# GCC 13+ ile
g++ -std=c++20 main.cpp -o main

# Clang 17+ ile
clang++ -std=c++20 main.cpp -o main

⚠️ Dikkat: std::format desteği derleyici sürümüne bağlıdır. GCC 13+, Clang 17+, MSVC 19.29+ (VS 2019 16.10+) tam destek sunar. Eski derleyicilerde fmt kütüphanesini kullanabilirsin (dersin sonunda anlatılıyor).


Format Specifiers: Tam Kontrol

Genel Syntax

Format specifier {} içinde : sonrasına yazılır:

{[argüman_indeksi]:[dolgu][hizalama][işaret][#][0][genişlik][.hassasiyet][tip]}

Karmaşık görünüyor ama parça parça öğrenince basit.

Genişlik ve Hizalama

#include <format>
#include <iostream>

int main() {
    // Genişlik: minimum karakter sayısı
    std::cout << std::format("[{:10}]", "Ali") << std::endl;
    // Çıktı: [Ali       ]  — sağda boşluk (string varsayılan: sola hizalı)

    std::cout << std::format("[{:10}]", 42) << std::endl;
    // Çıktı: [        42]  — solda boşluk (sayı varsayılan: sağa hizalı)

    // Hizalama: < sola, > sağa, ^ ortaya
    std::cout << std::format("[{:<10}]", 42) << std::endl;
    // Çıktı: [42        ]

    std::cout << std::format("[{:>10}]", "Ali") << std::endl;
    // Çıktı: [       Ali]

    std::cout << std::format("[{:^10}]", "orta") << std::endl;
    // Çıktı: [   orta   ]

    // Dolgu karakteri: hizalamadan önce
    std::cout << std::format("[{:*^10}]", "orta") << std::endl;
    // Çıktı: [***orta***]

    std::cout << std::format("[{:.<10}]", "Ali") << std::endl;
    // Çıktı: [Ali.......]

    std::cout << std::format("[{:0>8}]", 42) << std::endl;
    // Çıktı: [00000042]

    return 0;
}

Dolgu karakteri herhangi bir karakter olabilir — *, ., -, 0 veya başka bir şey. Hizalama ile birlikte kullanılır.

Hassasiyet (Precision)

#include <format>
#include <iostream>

int main() {
    double pi = 3.14159265358979;

    // Ondalık hassasiyet
    std::cout << std::format("{:.2f}", pi) << std::endl;   // 3.14
    std::cout << std::format("{:.4f}", pi) << std::endl;   // 3.1416
    std::cout << std::format("{:.0f}", pi) << std::endl;   // 3

    // Bilimsel gösterim
    std::cout << std::format("{:.3e}", pi) << std::endl;   // 3.142e+00
    std::cout << std::format("{:.3E}", pi) << std::endl;   // 3.142E+00

    // Genel (general) — gereksiz sıfırları atar
    std::cout << std::format("{:.4g}", pi) << std::endl;   // 3.142
    std::cout << std::format("{:.4g}", 1000.0) << std::endl; // 1000
    std::cout << std::format("{:.4g}", 100000.0) << std::endl; // 1e+05

    // String'lerde hassasiyet — karakter sınırı
    std::cout << std::format("{:.5}", "Merhaba Dunya") << std::endl;
    // Çıktı: Merha  (ilk 5 karakter)

    // Genişlik + hassasiyet birlikte
    std::cout << std::format("[{:10.2f}]", pi) << std::endl;
    // Çıktı: [      3.14]

    return 0;
}

İşaret ve Sıfır Dolgu

#include <format>
#include <iostream>

int main() {
    int pozitif = 42;
    int negatif = -42;

    // Varsayılan: sadece negatif işaret
    std::cout << std::format("{}", pozitif) << std::endl;   // 42
    std::cout << std::format("{}", negatif) << std::endl;   // -42

    // + : her zaman işaret göster
    std::cout << std::format("{:+}", pozitif) << std::endl; // +42
    std::cout << std::format("{:+}", negatif) << std::endl; // -42

    // boşluk: pozitifin önüne boşluk
    std::cout << std::format("{: }", pozitif) << std::endl; //  42
    std::cout << std::format("{: }", negatif) << std::endl; // -42

    // 0 ile sıfır dolgu (genişlikle birlikte)
    std::cout << std::format("{:05}", pozitif) << std::endl; // 00042
    std::cout << std::format("{:05}", negatif) << std::endl; // -0042

    return 0;
}

İşaret seçeneği tablo çıktılarında hizalama için çok kullanışlıdır. Pozitif ve negatif sayılar alt alta güzel dizilir.


Sayı Formatlama: Hex, Oct, Binary

Tamsayı Formatları

#include <format>
#include <iostream>

int main() {
    int sayi = 255;

    // Ondalık (varsayılan)
    std::cout << std::format("Decimal:  {}", sayi) << std::endl;
    // Çıktı: Decimal:  255

    // Onaltılık (hexadecimal)
    std::cout << std::format("Hex:      {:x}", sayi) << std::endl;
    // Çıktı: Hex:      ff

    std::cout << std::format("Hex (ust):{:X}", sayi) << std::endl;
    // Çıktı: Hex (ust):FF

    // Sekizlik (octal)
    std::cout << std::format("Octal:    {:o}", sayi) << std::endl;
    // Çıktı: Octal:    377

    // İkilik (binary)
    std::cout << std::format("Binary:   {:b}", sayi) << std::endl;
    // Çıktı: Binary:   11111111

    // # ile prefix göster
    std::cout << std::format("Hex:    {:#x}", sayi) << std::endl;
    // Çıktı: Hex:    0xff

    std::cout << std::format("Octal:  {:#o}", sayi) << std::endl;
    // Çıktı: Octal:  0377

    std::cout << std::format("Binary: {:#b}", sayi) << std::endl;
    // Çıktı: Binary: 0b11111111

    // Hex + sıfır dolgu + genişlik — bellek adresi gibi
    std::cout << std::format("Addr: {:#010x}", sayi) << std::endl;
    // Çıktı: Addr: 0x000000ff

    return 0;
}

Karakter ve Bool Formatlama

#include <format>
#include <iostream>

int main() {
    char c = 'A';
    bool aktif = true;

    // Karakter
    std::cout << std::format("Karakter: {}", c) << std::endl;   // A
    std::cout << std::format("ASCII:    {:d}", c) << std::endl;  // 65

    // Bool
    std::cout << std::format("Bool: {}", aktif) << std::endl;    // true
    std::cout << std::format("Sayi: {:d}", aktif) << std::endl;  // 1

    return 0;
}

Tip Güvenliği: Derleme Zamanı Kontrol

printf vs std::format

std::format'ın en büyük avantajı derleme zamanı tip kontrolüdür. Yanlış format string'i derleme hatası verir — runtime sürprizi olmaz.

#include <format>
#include <string>

void tipGuvenligi() {
    int yas = 25;
    std::string isim = "Ali";

    // printf: derlenir ama runtime'da UB
    // printf("%s\n", yas);     // %s ama int — çökme!
    // printf("%d\n", isim);    // %d ama string — çöp!

    // std::format: DERLEME HATASI
    // auto s1 = std::format("{:s}", yas);  // int'i string olarak formatlayamazsın
    // auto s2 = std::format("{:d}", isim); // string'i sayı olarak formatlayamazsın

    // Argüman sayısı uyumsuzluğu da derleme hatası
    // auto s3 = std::format("{} {} {}", yas);  // 3 placeholder, 1 argüman

    // Doğru kullanımlar
    auto s4 = std::format("{}", yas);          // 25
    auto s5 = std::format("{}", isim);         // Ali
    auto s6 = std::format("{} {}", isim, yas); // Ali 25
}

Bu kontrol C++20'de consteval ve std::basic_format_string altyapısı sayesinde mümkün oluyor. Format string'i derleme zamanında parse edilir ve argüman tipleriyle uyumu kontrol edilir.

Runtime Format String

Bazen format string'ini runtime'da belirlemen gerekir (örneğin bir konfigürasyon dosyasından okunan şablon). C++26'da std::runtime_format geliyor, ama şimdilik bu mümkün değil — format string'i derleme zamanı sabiti olmalı.

#include <format>
#include <string>

void runtimeFormat() {
    // Bu çalışır: derleme zamanı string literal
    auto s1 = std::format("Sayi: {}", 42);

    // Bu CALISMAZ: runtime string
    // std::string fmt_str = "Sayi: {}";
    // auto s2 = std::format(fmt_str, 42);  // Derleme hatası

    // C++26 ile gelecek:
    // auto s2 = std::format(std::runtime_format(fmt_str), 42);

    // Şimdilik alternatif: fmt kütüphanesi
    // auto s2 = fmt::format(fmt::runtime(fmt_str), 42);
}

Custom Type İçin Formatter

std::formatter Specialization

Kendi tiplerini std::format ile kullanabilmek için std::formatter template'ini özelleştirmen gerekir.

#include <format>
#include <iostream>
#include <string>

struct Nokta {
    double x;
    double y;
};

// std::formatter özelleştirmesi
template <>
struct std::formatter<Nokta> {
    // Format string'ini parse et
    constexpr auto parse(std::format_parse_context& ctx) {
        return ctx.begin();  // Ek format specifier yok
    }

    // Formatla ve yaz
    auto format(const Nokta& n, std::format_context& ctx) const {
        return std::format_to(ctx.out(), "({:.2f}, {:.2f})", n.x, n.y);
    }
};

int main() {
    Nokta p{3.14159, 2.71828};
    std::cout << std::format("Nokta: {}", p) << std::endl;
    // Çıktı: Nokta: (3.14, 2.72)

    std::cout << std::format("A={}, B={}", Nokta{0, 0}, Nokta{1, 1}) << std::endl;
    // Çıktı: A=(0.00, 0.00), B=(1.00, 1.00)

    return 0;
}

Format Specifier Destekleyen Formatter

Daha gelişmiş bir formatter, kullanıcının format specifier yazmasına izin verir:

#include <format>
#include <iostream>
#include <string>

struct Renk {
    uint8_t r, g, b;
};

template <>
struct std::formatter<Renk> {
    char sunum = 'd';  // 'd' = decimal, 'h' = hex

    constexpr auto parse(std::format_parse_context& ctx) {
        auto it = ctx.begin();
        if (it != ctx.end() && (*it == 'd' || *it == 'h')) {
            sunum = *it;
            ++it;
        }
        return it;
    }

    auto format(const Renk& r, std::format_context& ctx) const {
        if (sunum == 'h') {
            return std::format_to(ctx.out(), "#{:02X}{:02X}{:02X}",
                                  r.r, r.g, r.b);
        }
        return std::format_to(ctx.out(), "rgb({}, {}, {})",
                              r.r, r.g, r.b);
    }
};

int main() {
    Renk kirmizi{255, 0, 0};
    Renk mavi{0, 128, 255};

    // Varsayılan: decimal
    std::cout << std::format("Renk: {}", kirmizi) << std::endl;
    // Çıktı: Renk: rgb(255, 0, 0)

    // 'h' specifier: hex
    std::cout << std::format("Renk: {:h}", kirmizi) << std::endl;
    // Çıktı: Renk: #FF0000

    std::cout << std::format("Gokyuzu: {:h}", mavi) << std::endl;
    // Çıktı: Gokyuzu: #0080FF

    return 0;
}

parse metodu format string'indeki specifier'ları okur, format metodu o bilgiye göre çıktı üretir. Bu mekanizma sayesinde kendi tiplerini std::format ekosistemine tamamen entegre edebilirsin.


std::print (C++23)

format + output Birleşik

C++23 ile gelen std::print ve std::println, std::format ile std::cout'u birleştirir. Artık std::cout << std::format(...) yazmana gerek yok.

#include <print>
#include <string>
#include <vector>

int main() {
    std::string isim = "Ali";
    int yas = 25;

    // C++23: std::print — satır sonu yok
    std::print("Isim: {}, Yas: {}", isim, yas);

    // C++23: std::println — otomatik satır sonu
    std::println("Isim: {}, Yas: {}", isim, yas);

    // stderr'e yazdır
    std::print(stderr, "Hata: dosya bulunamadi: {}", "config.txt");

    // Tablo çıktısı
    std::println("{:<15} {:>5} {:>10}", "Urun", "Adet", "Fiyat");
    std::println("{:-<15} {:->5} {:->10}", "", "", "");
    std::println("{:<15} {:>5} {:>10.2f}", "Laptop", 3, 25999.99);
    std::println("{:<15} {:>5} {:>10.2f}", "Mouse", 50, 149.90);
    std::println("{:<15} {:>5} {:>10.2f}", "Klavye", 25, 399.00);

    return 0;
}

Çıktı:

Urun              Adet      Fiyat
--------------- ----- ----------
Laptop              3   25999.99
Mouse              50     149.90
Klavye             25     399.00

std::println inanılmaz temiz ve okunabilir kod üretir. std::cout ve << operatörünün karmaşasından tamamen kurtulursun.

💡 İpucu: std::print ve std::println GCC 14+, Clang 18+, MSVC 19.37+ ile desteklenir. Derleyicin desteklemiyorsa fmt::print aynı işlevi sunar.


Pratik Örnekler

Tablo Çıktısı

#include <format>
#include <iostream>
#include <string>
#include <vector>

struct Ogrenci {
    std::string isim;
    int numara;
    double ortalama;
};

void tabloYazdir(const std::vector<Ogrenci>& ogrenciler) {
    // Başlık
    std::cout << std::format("{:<20} {:>8} {:>10}\n",
                             "Isim", "Numara", "Ortalama");

    // Ayırıcı çizgi
    std::cout << std::format("{:-<20} {:->8} {:->10}\n", "", "", "");

    // Veriler
    for (const auto& o : ogrenciler) {
        std::cout << std::format("{:<20} {:>8} {:>10.2f}\n",
                                 o.isim, o.numara, o.ortalama);
    }

    // Toplam
    double toplam = 0;
    for (const auto& o : ogrenciler) toplam += o.ortalama;
    double genel = toplam / static_cast<double>(ogrenciler.size());

    std::cout << std::format("{:-<20} {:->8} {:->10}\n", "", "", "");
    std::cout << std::format("{:<20} {:>8} {:>10.2f}\n",
                             "GENEL ORTALAMA", "", genel);
}

int main() {
    std::vector<Ogrenci> sinif = {
        {"Ali Yilmaz", 1001, 3.45},
        {"Ayse Kaya", 1002, 3.78},
        {"Mehmet Demir", 1003, 2.90},
        {"Zeynep Celik", 1004, 3.92},
    };

    tabloYazdir(sinif);
    return 0;
}

Çıktı:

Isim                   Numara   Ortalama
-------------------- -------- ----------
Ali Yilmaz               1001       3.45
Ayse Kaya                1002       3.78
Mehmet Demir             1003       2.90
Zeynep Celik             1004       3.92
-------------------- -------- ----------
GENEL ORTALAMA                      3.51

Hex Dump

#include <format>
#include <iostream>
#include <string>
#include <vector>
#include <cstdint>
#include <cctype>

void hexDump(const std::vector<uint8_t>& data) {
    for (size_t i = 0; i < data.size(); i += 16) {
        // Adres
        std::cout << std::format("{:08x}: ", i);

        // Hex değerler
        for (size_t j = 0; j < 16; ++j) {
            if (i + j < data.size()) {
                std::cout << std::format("{:02x} ", data[i + j]);
            } else {
                std::cout << "   ";
            }
            if (j == 7) std::cout << " ";  // Orta boşluk
        }

        // ASCII gösterim
        std::cout << " |";
        for (size_t j = 0; j < 16 && i + j < data.size(); ++j) {
            char c = static_cast<char>(data[i + j]);
            std::cout << (std::isprint(c) ? c : '.');
        }
        std::cout << "|\n";
    }
}

int main() {
    std::string mesaj = "Hello, std::format! Bu bir hex dump ornegi.";
    std::vector<uint8_t> data(mesaj.begin(), mesaj.end());

    hexDump(data);
    return 0;
}

Çıktı:

00000000: 48 65 6c 6c 6f 2c 20 73  74 64 3a 3a 66 6f 72 6d  |Hello, std::form|
00000010: 61 74 21 20 42 75 20 62  69 72 20 68 65 78 20 64  |at! Bu bir hex d|
00000020: 75 6d 70 20 6f 72 6e 65  67 69 2e                  |ump ornegi.|

Bu tür bir çıktıyı printf ile yazmak mümkün, iostream ile çok uzun — std::format ikisinin arasında temiz bir denge kuruyor.


std::format vs printf vs iostream

Özellikprintfiostreamstd::format
Tip güvenliği❌ Yok✅ Var✅ Var (derleme zamanı)
Format string"%d %s"Manipülatörler"{} {}"
OkunabilirlikOrtaDüşük✅ Yüksek
Genişletilebilirlik❌ Zoroperator<<std::formatter
State sorunuYok⚠️ Kalıcı stateYok
Performans✅ HızlıYavaş✅ Hızlı
Locale desteğiKısıtlıVar{:L} ile
C++ standardıCC++98C++20
Derleme zamanı kontrolKısıtlıYok✅ Tam
std::string döndürmestd::ostringstream✅ Doğrudan

Tavsiye: Yeni C++20 projelerinde std::format (veya C++23 std::println) kullan. Legacy kodda printf kabul edilebilir. iostream'i formatlama için kullanmaktan kaçın — sadece basit çıktılar için yeterli.


fmt Kütüphanesi

std::format'ın İlham Kaynağı

std::format, Victor Zverovich'in {fmt} kütüphanesinden doğmuştur. C++20 öncesi projelerde veya derleyicin std::format desteklemiyorsa fmt kullanabilirsin.

Kurulum:

# vcpkg ile
vcpkg install fmt

# Conan ile
conan install fmt/10.2.1

# CMake FetchContent ile

CMake entegrasyonu:

include(FetchContent)
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG        10.2.1
)
FetchContent_MakeAvailable(fmt)

add_executable(main main.cpp)
target_link_libraries(main fmt::fmt)

Kullanım:

#include <fmt/core.h>
#include <fmt/color.h>
#include <fmt/ranges.h>
#include <vector>

int main() {
    // Temel — std::format ile aynı syntax
    fmt::print("Merhaba, {}!\n", "Dunya");

    // Renkli çıktı (std::format'ta yok!)
    fmt::print(fg(fmt::color::green), "Basarili!\n");
    fmt::print(fg(fmt::color::red) | fmt::emphasis::bold, "HATA!\n");

    // Range formatlama (std::format'ta yok!)
    std::vector<int> v = {1, 2, 3, 4, 5};
    fmt::print("Vektor: {}\n", v);
    // Çıktı: Vektor: [1, 2, 3, 4, 5]

    // Runtime format string (C++26'da gelecek)
    std::string fmt_str = "Sayi: {}";
    fmt::print(fmt::runtime(fmt_str), 42);

    return 0;
}

fmt, std::format'ın sahip olmadığı ekstra özellikler sunar: renkli çıktı, range formatlama, runtime format string'ler. Birçok büyük proje (Spdlog, Folly, Catch2) fmt'yi kullanır. std::format'a geçiş yapmak istersen sadece #include ve namespace'i değiştirmen yeterli — API aynı.


std::format_to ve Bellek Yönetimi

Buffer'a Yazma

std::format her çağrıda yeni bir std::string oluşturur. Performans kritik durumlarda std::format_to ile mevcut bir buffer'a yazabilirsin.

#include <format>
#include <iostream>
#include <string>
#include <vector>

int main() {
    // format_to: mevcut container'a yaz
    std::string buffer;
    buffer.reserve(256);  // Önceden yer ayır

    std::format_to(std::back_inserter(buffer),
                   "Isim: {}, ", "Ali");
    std::format_to(std::back_inserter(buffer),
                   "Yas: {}, ", 25);
    std::format_to(std::back_inserter(buffer),
                   "Puan: {:.1f}", 95.5);

    std::cout << buffer << std::endl;
    // Çıktı: Isim: Ali, Yas: 25, Puan: 95.5

    // format_to_n: maksimum karakter sayısıyla sınırla
    char kucuk_buf[20];
    auto result = std::format_to_n(kucuk_buf, sizeof(kucuk_buf) - 1,
                                    "Merhaba, {}!", "Dunya");
    *result.out = '\0';  // Null terminate

    std::cout << kucuk_buf << std::endl;
    // Çıktı: Merhaba, Dunya!

    // formatted_size: kaç karakter gerektiğini hesapla (yazmadan)
    auto boyut = std::formatted_size("Sayi: {:010.3f}", 3.14);
    std::cout << "Gereken boyut: " << boyut << std::endl;

    return 0;
}

std::format_to döngü içinde çok sayıda formatlama yaparken gereksiz bellek tahsisini önler. Log sistemleri ve seri port çıktıları gibi yerlerde fark yaratır.


Özet

  • printf tip güvenliği sunmaz ve format string hatalarını runtime'da sessizce geçer — yeni kodda tercih etme

  • std::format derleme zamanı tip kontrolü, temiz syntax ve yüksek performansla C++20'nin string formatlama standardıdır

  • Format specifier'lar ({:>10.2f}) ile genişlik, hizalama, dolgu, hassasiyet ve sayı tabanı tam kontrol altında

  • Custom type'lar için std::formatter specialization yazarak kendi tiplerini std::format ekosistemine entegre edebilirsin

  • std::print/println (C++23) format + output'u birleştirerek std::cout << ihtiyacını ortadan kaldırır

  • Derleyicin C++20 desteklemiyorsa fmt kütüphanesi aynı API'yi sunar ve ekstra özellikler (renk, range) içerir