C++20 Biçimlendirme Kitaplığı: Kullanıcı tanımlı veri türlerini biçimlendirme

Adanali

Active member
C++20 Biçimlendirme Kitaplığı: Kullanıcı tanımlı veri türlerini biçimlendirme


  1. C++20 Biçimlendirme Kitaplığı: Kullanıcı tanımlı veri türlerini biçimlendirme












Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.







Daha önceki yazılarımda temel türlerden ve std::string biçimlendirilmiş:

Duyuru



Şimdi dikkatimi özel tür biçimlendirmesine çevireceğim.

std::formatter Özel veri türlerinin biçimlendirilmesine izin verir. Bunu yapmak için kursa katılmalısınız. std::formatter özelleştirilmiş tip için uzmanlaşmıştır. Özellikle üye fonksiyonlarının parse VE format uygulanacaktır.

  • parse: Bu işlev biçim dizesini ayrıştırır ve bir hata oluşursa bir tane döndürür std::format_error dıştan.
İşlev parse Meli constexpr derleme zamanı ayrıştırmayı etkinleştirmek için. Bir analiz bağlamını kabul edin (std::format_parse_context) ve format belirticinin son karakterini döndürmelidir (kapanış ayracı }). Biçim belirticiyi kullanmazsanız bu aynı zamanda biçim belirticinin ilk karakteri olacaktır.

Aşağıdaki satırlarda format belirticinin ilk karakterinin bazı örnekleri gösterilmektedir:


"{}" // context.begin() points to }
"{:}" // context.begin() points to }
"{0:d}" // context.begin() points to d} 
"{:5.3f}" // context.begin() points to: 5.3f}
"{:x}" // context.begin() points to x} 
"{0} {0}" // context.begin() points to: "} {0}"


context.begin() e format belirleyicisinin ilk karakterini işaret eder context.end() tüm biçim dizesinin son karakterine kadar. Bir biçim belirtici belirtmezseniz aradaki her şeyi kullanmanız gerekir. context.begin() VE context.end() analiz ve sonun konumu } geri vermek.

  • format: Bu fonksiyon sabit olmalıdır. Değerini anlıyor val ve formatın bağlamı context. formatdeğeri biçimlendir val ve analiz edilen formata göre yazar context.out(). Dönüş değeri context.out() doğrudan girebilir std::format_to yerleştirilmelidir. std::format_to daha fazla çıktı için yeni konuma dönmelidir. Çıktının sonunu temsil eden bir yineleyici döndürür.
Teorik olarak bu kadar. Şimdi bunun nasıl uygulanabileceğini göstermek için örnekler kullanmak istiyorum.

Tek değer biçimlendirici



// formatSingleValue.cpp

#include <format>&#13;
#include <iostream>&#13;
&#13;
class SingleValue { // (1)&#13;
public: &#13;
SingleValue() = default;&#13;
explicit SingleValue(int s): singleValue{s} {}&#13;
int getValue() const { // (2)&#13;
return singleValue;&#13;
}&#13;
private:&#13;
int singleValue{};&#13;
};&#13;
&#13;
template<> // (3)&#13;
struct std::formatter<SingleValue> {&#13;
constexpr auto parse(std::format_parse_context& context) { // (4)&#13;
return context.begin();&#13;
}&#13;
auto format(const SingleValue& sVal, std::format_context& context) const { // (5)&#13;
return std::format_to(context.out(), "{}", sVal.getValue());&#13;
}&#13;
};&#13;
&#13;
int main() {&#13;
&#13;
std::cout << 'n'; &#13;
&#13;
SingleValue sVal0;&#13;
SingleValue sVal2020{2020};&#13;
SingleValue sVal2023{2023};&#13;
&#13;
std::cout << std::format("Single Value: {} {} {}n", sVal0, sVal2020, sVal2023);&#13;
std::cout << std::format("Single Value: {1} {1} {1}n", sVal0, sVal2020, sVal2023);&#13;
std::cout << std::format("Single Value: {2} {1} {0}n", sVal0, sVal2020, sVal2023);&#13;
&#13;
std::cout << 'n';&#13;
&#13;
}


SingleValue (Satır 1) yalnızca bir değeri olan bir sınıftır. Üye işlevi getValue (Satır 2) bu değeri döndürür. ben uzmanlaştım std::formatter (satır 3) için SingleValue. Bu uzmanlığın işlevleri vardır parse (satır 4) e format (satır 5). parse format spesifikasyonunun sonunu döndürür. Format spesifikasyonunun sonu sonuncusudur }. format e değerini biçimlendir context.out öyle bir nesne yaratır ki std::format_to teslim edildi. format daha fazla çıktı için yeni konumu döndürür.

Bu programı çalıştırmak beklenen sonucu verir:








Ancak bu formatlayıcının ciddi bir dezavantajı vardır: herhangi bir format spesifikasyonunu desteklemez. Bir sonraki örnekte bunu geliştireceğim.

Biçim belirticiyi destekleyen bir biçimlendirici


Biçimlendirici için standart bir biçimlendirici kullanıyorsanız, özel bir veri türü için biçimlendirici uygulamak oldukça basittir. Standart biçimlendiriciyi kullanmanın iki yolu vardır: yetki verme ve devralma.

heyet


Aşağıdaki formatlayıcı görevini standart bir formatlayıcıya devreder.


// formatSingleValueDelegation.cpp&#13;
&#13;
#include <format>&#13;
#include <iostream>&#13;
&#13;
class SingleValue {&#13;
public: &#13;
SingleValue() = default;&#13;
explicit SingleValue(int s): singleValue{s} {}&#13;
int getValue() const {&#13;
return singleValue;&#13;
}&#13;
private:&#13;
int singleValue{};&#13;
};&#13;
&#13;
template<> // (1)&#13;
struct std::formatter<SingleValue> {&#13;
&#13;
std::formatter<int> formatter; // (2)&#13;
&#13;
constexpr auto parse(std::format_parse_context& context) {&#13;
return formatter.parse(context); // (3)&#13;
}&#13;
&#13;
auto format(const SingleValue& singleValue, std::format_context& context) const {&#13;
return formatter.format(singleValue.getValue(), context); // (4)&#13;
}&#13;
&#13;
}; &#13;
&#13;
int main() {&#13;
&#13;
std::cout << 'n'; &#13;
&#13;
SingleValue singleValue0;&#13;
SingleValue singleValue2020{2020};&#13;
SingleValue singleValue2023{2023};&#13;
&#13;
std::cout << std::format("{:*<10}", singleValue0) << 'n';&#13;
std::cout << std::format("{:*^10}", singleValue2020) << 'n';&#13;
std::cout << std::format("{:*>10}", singleValue2023) << 'n';&#13;
&#13;
std::cout << 'n';&#13;
&#13;
}


std::formatter<SingleValue> (Satır 1) için varsayılan bir biçimlendirici vardır. int: std::formatter<int> formatter (Hat 2). Ayrıştırma işini formatlayıcıya devrediyorum (3. satır). Sonuç olarak, biçimlendirme işi biçimlendiriciye devredilir (satır 4).

Program çıktısı, formatlayıcının dolgu ve hizalamayı desteklediğini gösterir.








Miras


Kalıtım sayesinde kullanıcı tanımlı veri tipi için formatlayıcı uygulaması başarılıdır SingleValue çok kolay.


// formatSingleValueInheritance.cpp&#13;
&#13;
#include <format>&#13;
#include <iostream>&#13;
&#13;
class SingleValue {&#13;
public: &#13;
SingleValue() = default;&#13;
explicit SingleValue(int s): singleValue{s} {}&#13;
int getValue() const {&#13;
return singleValue;&#13;
}&#13;
private:&#13;
int singleValue{};&#13;
};&#13;
&#13;
template<>&#13;
struct std::formatter<SingleValue> : std::formatter<int> { // (1)&#13;
auto format(const SingleValue& singleValue, std::format_context& context) const {&#13;
return std::formatter<int>::format(singleValue.getValue(), context);&#13;
}&#13;
};&#13;
&#13;
int main() {&#13;
&#13;
std::cout << 'n'; &#13;
&#13;
SingleValue singleValue0;&#13;
SingleValue singleValue2020{2020};&#13;
SingleValue singleValue2023{2023};&#13;
&#13;
std::cout << std::format("{:*<10}", singleValue0) << 'n';&#13;
std::cout << std::format("{:*^10}", singleValue2020) << 'n';&#13;
std::cout << std::format("{:*>10}", singleValue2023) << 'n';&#13;
&#13;
std::cout << 'n';&#13;
&#13;
}


öncülük ederim std::formatter<SingleValue> itibaren std::formatter<int> (satır 1)'den. Yalnızca format işlevini uygulamanız gerekir. Bu programın çıktısı önceki programın çıktısıyla aynıdır. formatSingleValueDelegation.cpp.

Standart bir formatlayıcıdan yetki vermek veya devralmak, özel bir formatlayıcıyı uygulamanın kolay bir yoludur. Bu strateji yalnızca değeri olan özel veri türleri için işe yarar.

Sıradaki ne?


Bir sonraki yazımda birden fazla değere sahip özel bir veri türü için formatlayıcı uygulayacağım.


(harita)



Haberin Sonu
 
Üst