C ++ 20'de biçimlendirme kitaplığı: Kullanıcı tarafından tanımlanan veri türleri türleri

Adanali

Active member
C ++ 20'de biçimlendirme kitaplığı: Kullanıcı tarafından tanımlanan veri türleri türleri


  1. C ++ 20'de biçimlendirme kitaplığı: Kullanıcı tarafından tanımlanan veri türleri türleri












Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.







Önceki makalelerimde temel türlerim var ve std::string Formatte:



Şimdi kendimi kişiselleştirilmiş türlerin biçimlendirmesine adadım.

std::formatter Kullanıcı tarafından tanımlanan veri türlerinin biçimini etkinleştirin. Yapmalısın std::formatter Kişiselleştirilmiş tip için uzmanlaşmıştır. Özellikle, üyenin işlevleri parse VE format uygulanacak.

  • parse: Bu işlev biçim halkasını analiz etti ve hata durumunda bir tane verir. std::format_error dışında.
İşlev parse Zorunlu constexpr derleme dönemi için analizi etkinleştirmek. Bir analiz bağlamını kabul eder (std::format_parse_context) ve belirli formatın son işaretini döndürmelidir (dost kapanış bandı }). Belirli bir formatı kullanmadan yaparsanız, bu da formatın ilk işaretidir.



Aşağıdaki satırlar, belirli formatın ilk işaretinin bazı örneklerini göstermektedir:


"{}" // 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() Form tanımlayıcısının ilk işaretine işaret ediyor e context.end() Tüm biçim halkasının son işaretinde. Bir format tanımlayıcısını belirtmezseniz, her şeyi arasında her şeyi yapmanız gerekir. context.begin() VE context.end() Parsen ve finalin pozisyonu } geri dönmek.

  • format: Bu işlev sabit olmalıdır. Değeri alır val ve formatın bağlamı context. formatdeğeri biçimlendir val ve kaydedilen formata göre yazın context.out(). Dönüş değeri context.out() Doğrudan içinde olabilir std::format_to eklenebilir. std::format_to Daha ileri baskı için yeni pozisyonu döndürmelidir. Baskının sonunu temsil eden retro bir yineleyici var.
Teori için çok fazla. Şimdi nasıl kullanılabileceğini göstermek için örnekler kullanmak istiyorum.

Tek bir değer için bir format



// 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) sadece bir değeri olan bir sınıftır. Üye işlevi getValue (Satır 2) bu değeri döndürür. Uzmanım std::formatter (Satır 3) için SingleValue. Bu uzmanlığın işlevleri var parse (Satır 4) e format (Satır 5). parse Biçim biçiminin sonunu döndürür. Formatın formatının sonu sondur }. format E değerini biçimlendir context.out Bulunan Bir Nesne Oluştur std::format_to teslim edilir. format Daha fazla baskı için yeni konumu döndürür.

Bu programın yürütülmesi beklenen sonucu sağlar:








Bununla birlikte, bu forma Constantor'un ciddi bir dezavantajı vardır: formatın herhangi bir spesifikasyonunu desteklemez. Bir sonraki örnekte geliştireceğim.

Belirli bir biçimi destekleyen bir biçim


Biçim için standart bir biçim kullanıyorsanız, kişiselleştirilmiş bir veri türü için bir formatın uygulanması oldukça basittir. Standart bir biçim kullanmanın iki yolu vardır: heyet ve miras.

delegasyon


Aşağıdaki forma, görevini standart bir formatöre devrediyor.


// 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 standart bir biçimlendirme vardır int: std::formatter<int> formatter (Satır 2). Analiz sırasını biçime devredin (satır 3). Sonuç olarak, formattaki biçimlendirme sırası devredilir (satır 4).

Programın çıktısı, formatı ve hizalamayı doldurma belirtilerinin desteklendiğini gösterir.








Miras


Miras sayesinde, kişiselleştirilmiş veri türü için formatın uygulanması SingleValue Çok hafif.


// 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;
}


Yönetmek std::formatter<SingleValue> itibaren std::formatter<int> (satır 1). Yalnızca format işlevi uygulanmalıdır. Bu programın çıktısı, önceki programın çıktısı ile aynıdır formatSingleValueDelegation.cpp.

Standart bir formata veya masalın varisine devredilmesi, kişiselleştirilmiş bir format uygulamanın kolay bir yoludur. Bu strateji yalnızca değeri olan kişiselleştirilmiş veri türleri için çalışır.

Sırada ne var?


Bir sonraki yazımda, birden fazla değere sahip bir tür kişiselleştirilmiş veri için bir format uygulayacağım.


(harita)
 
Üst