Peter Gottschling は、彼の最後の投稿「C++20 の std::format」で、C++20 の新しい書式設定ライブラリの基本を紹介しました。今日の投稿では、Peter がユーザー定義型のフォーマットについて書いています。
テンプレートの特殊化の最初の例は、ユーザー タイプをサポートするために導入された新しいフォーマット ライブラリのカスタマイズです。
ユーザー定義型の書式設定
たとえば、 dmc::vector
を選択します (dmc
は、著者による本「Discovering Modern C++」の名前空間です) 単一の値の書式設定を指定したいクラスです。さらに、フォーマット文字列に文字 'c'
が含まれている場合、囲み括弧を中括弧に置き換えたいと考えています。 .この目的のために、クラス std::formatter
を特殊化する必要があります。 (または fmt::formatter
プロトタイプ ライブラリ fmt
の場合 )。私たちの特殊化には、メソッド parse
が含まれます と format
.
前者から始めましょう:
template <typename Value> struct formatter<dmc::vector<Value>> { constexpr auto parse(format_parse_context& ctx) { value_format= "{:"; for (auto it= begin(ctx); it != end(ctx); ++it) { char c= *it; if (c == 'c') curly= true; else value_format+= c; if (c == '}') return it; } return end(ctx); } // ... bool curly{false}; std::string value_format; };
引数として、begin
の構文解析コンテキストが与えられます。 iterator は、フォーマット指定の最初の文字を指します。つまり、~コロンの後の最初の文字であり、コロンがない場合は左中括弧の後の最初の文字です。フォーマット仕様をローカルの value_format,
とほぼ同じようにコピーします 特殊文字 'c'
のみ スキップされます。簡単にするために、次の右中括弧がフォーマット文字列を終了するように、形式には左中括弧または右中括弧が含まれていないと仮定します。最後に、右中括弧または終了イテレータを指すイテレータを返します。
この情報を使用して、vector
を出力できます。 メソッド format
で :
template <typename Value> struct formatter<dmc::vector<Value>> { template <typename FormatContext> auto format(const dmc::vector<Value>& v, FormatContext& ctx) { auto&& out= ctx.out(); format_to(out, curly ? "{{" : "["); if (v.size() > 0) format_to(out, value_format, v[0]); for (int i= 1; i < v.size(); ++i) format_to(out, ", " + value_format, v[i]); return format_to(out, curly ? "}}" : "]"); } // ... };
まず、出力バッファへの参照を取得します。次に、開始ブレースまたはブラケットをそれに書き込みます。 format
ではブレースには特別な意味があるため、 ライブラリでは、二重中括弧のエスケープ シーケンスが必要です。残りの出力は ostream
と同等です 出力。最後に、出力バッファを返します。
これで、さまざまな形式を試すことができます:
dmc::vector<double> v{1.394, 1e9, 1.0/3.0, 1e-20}; print("v with empty format = {:}.\n", v); print("v with f = {:f}.\n", v); print("v curly with f = {:fc}.\n", v); print("v width 9, 4 digits = {:9.4f}.\n", v); print("v scient. = {:ec}.\n", v);
出力に応じて表示されます:
v with empty format = [1.394, 1000000000.0, 0.3333333333333333, 1e-20]. v with f = [1.394000, 1000000000.000000, 0.333333, 0.000000]. v curly with f = {1.394000, 1000000000.000000, 0.333333, 0.000000}. v width 9, 4 digits = [ 1.3940, 1000000000.0000, 0.3333, 0.0000]. v scient. = {1.394000e+00, 1.000000e+09, 3.333333e-01, 1.000000e-20}.
全体として、新しいフォーマットは次のとおりです:
- コンパクト :上記の例で示されています
- 適応性: さまざまな出力オーダーへ
- タイプセーフ :引数が一致しない場合、例外がスローされます
- 拡張可能 :ユーザー定義型に拡張できます
これらの理由から、これは前述の手法よりも優れているため、十分なコンパイラ サポートが利用可能になったらすぐに使用することを強くお勧めします。
std::format
を簡潔に紹介してくれた Peter Gottschling にもう一度感謝します。 .フォーマッティング ライブラリの紹介を完成させるために、いくつかの単語を追加させてください。
試してみる
Peter が既に述べたように、GitHub は fmt
をホストしていました。 library は、C++20 の新しい書式設定ライブラリのプロトタイプです。 fmt
のフロント ページ プロジェクトには、いくつかの簡単な例とパフォーマンスの数値が含まれています。これらの例には、例を実行するためのコンパイラ エクスプローラへの直接リンクが含まれています。
新しいフォーマット ライブラリのおかげで、chrono
の期間を表示できます。 ライブラリ:
#include <fmt/chrono.h> int main() { using namespace std::literals::chrono_literals; fmt::print("Default format: {} {}\n", 42s, 100ms); fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); }
コンパイラ エクスプローラーでプログラムを実行すると、次の出力が得られます。
C++20 への移植
fmt
から以前のプログラムを移植 C++20 フォーマット ライブラリは簡単です。 C++ 標準ヘッダー chrono
を使用する必要があります と iostream
.さらに、呼び出し fmt::print
を置き換えます 関数 std::format
で 結果を std::cout
にプッシュします . std::format
指定されたフォーマット文字列とオプションのローカルに従って文字列を返します。
// formatChrono.cpp #include <chrono> #include <iostream> int main() { using namespace std::literals::chrono_literals; std::cout << std::format("Default format: {} {}\n", 42s, 100ms) << "\n"; std::cout << std::format("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s) << "\n"; }
次は?
次回の投稿では、引き続き便利な機能について説明します。 C++20 では、2 つの値の中点を計算できます。std::string
かどうかを確認してください。 部分文字列で開始または終了し、 std::bind_front
で callable を作成します .