最新のC ++のコンパイル段階で文字列を操作する







C ++でプログラミングしている場合、2つの文字列リテラルを比較したり、それらの連結を実行したりできないのはなぜだろうと思われます。







auto str = "hello" + "world"; //   if ("hello" < "world") { // ,    ,   // ... }
      
      





しかし、彼らが言うように、「それは不可能ですが、あなたが本当にしたいなら、あなたはできます。」 ステレオタイプを切り捨て、コンパイルの段階で壊します。







なぜこれがすべて必要なのか



私が取り組んでいたプロジェクトの1つでは、文字列定数としてstd :: stringを使用するのが習慣でした。 プロジェクトには、グローバル文字列定数が定義されたいくつかのモジュールがありました。







 // plugin.h const std::string PLUGIN_PATH = "/usr/local/lib/project/plugins/"; // ...
      
      





 // sample_plugin.h const std::string SAMPLE_PLUGIN_LIB = PLUGIN_PATH + "sample.so"; // ...
      
      





ある日、あなたはすでに何が起こったのかすでに推測していると思います。 SAMPLE_PLUGIN_PATH



の値は"sample.so"



PLUGIN_PATH



が、 PLUGIN_PATH



の値は"/usr/local/lib/project/plugins/"



でしたが、予想どおりでした。 これはどうして起こるのでしょうか? すべてが非常に簡単で、グローバルオブジェクトの初期化の順序は定義されていません。初期化時にSAMPLE_PLUGIN_PATH



変数PLUGIN_PATH



が空でした。







さらに、このアプローチには多くの欠点があります。 まず、グローバルオブジェクトの作成時にスローされる例外はキャッチされません。 第二に、初期化はプログラムの実行中に発生し、貴重なプロセッサー時間を消費します。







そのとき、コンパイル段階で文字列を操作するというアイデアを思いつき、最終的にこの記事を執筆しました。







この記事では、コンパイル段階で操作できる行を検討します。 このような行を静的と呼びます。







実装されたすべての操作は、静的文字列を操作するためのライブラリに含まれていました。 ライブラリのソースコードはgithubで入手できます。リンクは記事の最後にあります。







ライブラリを使用するには、少なくともC ++ 14が必要です。







静的な文字列定義



静的文字列を文字の配列として定義します;便宜上、文字列は常にヌル文字で終わると仮定します:







 template<size_t Size> using static_string = std::array<const char, Size>; constexpr static_string<6> hello = {'H', 'e', 'l', 'l', 'o', '\0'};
      
      





ここで別の方法で、文字列を文字のタプルとして定義できます。 このオプションは、より面倒で便利ではないように思えました。 したがって、ここでは考慮しません。







静的な文字列を作成する



上記のこんにちは行の定義を見てください、それはひどいです。 最初に、配列の長さを事前に計算する必要があります。 第二に、最後にヌル文字を書くことを忘れないでください。 第三に、これらすべてのコンマ、括弧、引用符。 間違いなくこれで何かをする必要があります。 私はこのような何かを書きたいです:







 constexpr auto hello = make_static_string("hello");
      
      





ここで、変数テンプレートの形式の1つが役立ちます。これにより、文字列リテラルから静的文字列の集計初期化のインデックスとしてテンプレート引数を拡張できます。







 template<size_t Size, size_t ... Indexes> constexpr static_string<sizeof ... (Indexes) + 1> make_static_string(const char (& str)[Size]) { return {str[Indexes] ..., '\0'}; } constexpr auto hello = make_static_string<0, 1, 2, 3, 4>("hello"); // hello == "hello"
      
      





すでに優れていますが、インデックスはまだ手書きで作成する必要があります。 ここで、すべてのインデックスを指定しない場合は、文字列リテラルのサブストリングを取得でき、逆の順序で書き込むと、その逆になることに注意してください。







 constexpr hello1 = make_static_string<1, 2, 3>("hello"); // hello1 == "ell" constexpr hello2 = make_static_string<4, 3, 2, 1, 0>("hello"); // hello2 == "olleh"
      
      





この考慮事項は、今後非常に役立ちます。







次に、何らかの方法で行インデックスのシーケンスを生成する必要があります。 これを行うには、継承のトリックを適用します。 必要なインデックスのセットをテンプレートパラメータとして空の構造を定義します(何かを継承する必要があります)。







 template<size_t ... Indexes> struct index_sequence {};
      
      





インデックスを一度に1つずつ生成するジェネレーター構造を定義し、最初のパラメーターにカウンターを格納します。







 template<size_t Size, size_t ... Indexes> struct make_index_sequence : make_index_sequence<Size - 1, Size - 1, Indexes ...> {};
      
      





また、すべてのインデックスが生成された場合(カウンターがゼロ)、カウンターを破棄し、ジェネレーターが必要なシーケンスに変わります。再帰エンドポイントも処理します。







 template<size_t ... Indexes> struct make_index_sequence<0, Indexes ...> : index_sequence<Indexes ...> {};
      
      





その結果、静的文字列を作成する機能は次のようになります。







 template<size_t Size, size_t ... Indexes> constexpr static_string<sizeof ... (Indexes) + 1> make_static_string(const char (& str)[Size], index_sequence<Indexes ...>) { return {str[Indexes] ..., '\0'}; }
      
      





静的な文字列に対して同様の関数を記述します。これはさらに役立ちます。







 template<size_t Size, size_t ... Indexes> constexpr static_string<sizeof ... (Indexes) + 1> make_static_string(const static_string<Size>& str, index_sequence<Indexes ...>) { return {str[Indexes] ..., '\0'}; }
      
      





さらに、文字列リテラルfoo(const char (& str)[Size])



を受け入れる各関数に対して、静的文字列foo(const static_string<Size>& str)



同様の関数を記述しfoo(const static_string<Size>& str)



。 しかし、簡潔にするために、これについては言及しません。







文字列リテラルの長さはわかっているため、一連のインデックスを自動的に生成できます。上記の関数のラッパーを作成します。







 template<size_t Size> constexpr static_string<Size> make_static_string(const char (& str)[Size]) { return make_static_string(str, make_index_sequence<Size - 1>{}); }
      
      





この関数を使用すると、章の最初で望んでいたことを正確に行うことができます。







引数がない場合は、空の静的文字列を返します。これは、ヌル文字のみで構成されます。







 constexpr static_string<1> make_static_string() { return {'\0'}; }
      
      





また、文字のタプルから文字列を作成する必要があります。







 template<char ... Chars> constexpr static_string<sizeof ... (Chars) + 1> make_static_string(char_sequence<Chars ...>) { return {Chars ..., '\0'}; }
      
      





ちなみに、この記事で後述するものはすべて、この章で説明する手法に基づいています。 したがって、理解できないものが残っている場合は、もう一度章を読み直すことをお勧めします。







静的文字列をストリームに出力します



ここではすべてが簡単です。 文字列はヌル文字で終わるため、配列データをストリームに出力するだけで十分です。







 template<size_t Size> std::ostream& operator<<(std::ostream& os, const static_string<Size>& str) { os << str.data(); return os; }
      
      





静的文字列をstd :: stringに変換します



ここでも複雑なことはありません。 文字列を配列データで初期化します:







 template<size_t Size> std::string to_string(const static_string<Size>& str) { return std::string(str.data()); }
      
      





静的な文字列の比較



違いが見つかるまで、または少なくとも1行が終わるまで、文字ごとに行を比較します。 constexpr forはまだ発明されていないため、再帰と三項演算子を使用します。







 template<size_t Size1, size_t Size2> constexpr int static_string_compare( const static_string<Size1>& str1, const static_string<Size2>& str2, int index = 0) { return index >= Size1 && index >= Size2 ? 0 : index >= Size1 ? -1 : index >= Size2 ? 1 : str1[index] > str2[index] ? 1 : str1[index] < str2[index] ? -1 : static_string_compare(str1, str2, index + 1); }
      
      





将来的には、コンパレータの拡張バージョンが必要になり、各行に個別のインデックスを導入し、比較する文字の数も制限します。







 template<size_t Size1, size_t Size2> constexpr int static_string_compare( const static_string<Size1>& str1, size_t index1, const static_string<Size2>& str2, size_t index2, size_t cur_length, size_t max_length) { return cur_length > max_length || (index1 >= Size1 && index2 >= Size2) ? 0 : index1 >= Size1 ? -1 : index2 >= Size2 ? 1 : str1[index1] > str2[index2] ? 1 : str1[index1] < str2[index2] ? -1 : static_string_compare(str1, index1 + 1, str2, index2 + 1, cur_length + 1, max_length); }
      
      





このバージョンのコンパレータを使用すると、文字列全体だけでなく、個々の部分文字列も比較できます。







静的な文字列の連結



連結には、静的文字列の作成に関する章と同じ変数テンプレートを使用します。 最初の行の文字(ヌル文字を除く)で配列を最初に初期化し、次に2番目の行で文字列を初期化し、最後にヌル文字を最後に追加します。







 template<size_t Size1, size_t ... Indexes1, size_t Size2, size_t ... Indexes2> constexpr static_string<Size1 + Size2 - 1> static_string_concat_2( const static_string<Size1>& str1, index_sequence<Indexes1 ...>, const static_string<Size2>& str2, index_sequence<Indexes2 ...>) { return {str1[Indexes1] ..., str2[Indexes2] ..., '\0'}; } template<size_t Size1, size_t Size2> constexpr static_string<Size1 + Size2 - 1> static_string_concat_2( const static_string<Size1>& str1, const static_string<Size2>& str2) { return static_string_concat_2(str1, make_index_sequence<Size1 - 1>{}, str2, make_index_sequence<Size2 - 1>{}); }
      
      





また、任意の数の文字列または文字列リテラルを連結するための変数テンプレートを実装します。







 constexpr auto static_string_concat() { return make_static_string(); } template<typename Arg, typename ... Args> constexpr auto static_string_concat(Arg&& arg, Args&& ... args) { return static_string_concat_2(make_static_string(std::forward<Arg>(arg)), static_string_concat(std::forward<Args>(args) ...)); }
      
      





静的文字列検索操作



静的文字列で文字と部分文字列を検索する操作を検討してください。







静的な文字列で文字を検索する



文字検索は特に難しくありません。すべてのインデックスの文字を再帰的にチェックし、一致する場合は最初のインデックスを返します。 また、検索の初期位置と一致のシーケンス番号を設定する機会を提供します。







 template<size_t Size> constexpr size_t static_string_find(const static_string<Size>& str, char ch, size_t from, size_t nth) { return Size < 2 || from >= Size - 1 ? static_string_npos : str[from] != ch ? static_string_find(str, ch, from + 1, nth) : nth > 0 ? static_string_find(str, ch, from + 1, nth - 1) : from; }
      
      





定数static_string_npos



は、検索が失敗したことを示します。 次のように定義します。







 constexpr size_t static_string_npos = std::numeric_limits<size_t>::max();
      
      





同様に、逆方向に検索を実装します。







 template<size_t Size> constexpr size_t static_string_rfind(const static_string<Size>& str, char ch, size_t from, size_t nth) { return Size < 2 || from > Size - 2 ? static_string_npos : str[from] != ch ? static_string_rfind(str, ch, from - 1, nth) : nth > 0 ? static_string_rfind(str, ch, from - 1, nth - 1) : from; }
      
      





静的文字列内の文字の出現を決定する



文字の出現を判断するには、それを探してみてください。







 template<size_t Size> constexpr bool static_string_contains(const static_string<Size>& str, char ch) { return static_string_find(str, ch) != static_string_npos; }
      
      





静的文字列内の文字の出現回数をカウントします



エントリ数のカウントは簡単に実装されます。







 template<size_t Size> constexpr size_t static_string_count(const static_string<Size>& str, char ch, size_t index) { return index >= Size - 1 ? 0 : (str[index] == ch ? 1 : 0) + static_string_count(str, ch, index + 1); }
      
      





静的文字列で部分文字列を検索します



静的行は比較的小さいと想定されるため、ここではKnut-Morris-Prattアルゴリズムを実装せず、最も単純な2次アルゴリズムを実装します。







 template<size_t Size, size_t SubSize> constexpr size_t static_string_find(const static_string<Size>& str, const static_string<SubSize>& substr, size_t from, size_t nth) { return Size < SubSize || from > Size - SubSize ? static_string_npos : static_string_compare(str, from, substr, 0, 1, SubSize - 1) != 0 ? static_string_find(str, substr, from + 1, nth) : nth > 0 ? static_string_find(str, substr, from + 1, nth - 1) : from; }
      
      





同様に、逆方向に検索を実装します。







 template<size_t Size, size_t SubSize> constexpr size_t static_string_rfind(const static_string<Size>& str, const static_string<SubSize>& substr, size_t from, size_t nth) { return Size < SubSize || from > Size - SubSize ? static_string_npos : static_string_compare(str, from, substr, 0, 1, SubSize - 1) != 0 ? static_string_rfind(str, substr, from - 1, nth) : nth > 0 ? static_string_rfind(str, substr, from - 1, nth - 1) : from; }
      
      





静的文字列内の部分文字列の出現を決定する



部分文字列の出現を判断するには、それを探してみてください。







 template<size_t Size, size_t SubSize> constexpr bool static_string_contains(const static_string<Size>& str, const static_string<SubSize>& substr) { return static_string_find(str, substr) != static_string_npos; }
      
      





静的文字列が特定の部分文字列で/で始まる/終わるかどうかを判断する



前述のコンパレータを適用すると、静的文字列が指定された部分文字列で始まるかどうかを判断できます。







 template<size_t SubSize, size_t Size> constexpr bool static_string_starts_with(const static_string<Size>& str, const static_string<SubSize>& prefix) { return SubSize > Size ? false : static_string_compare(str, 0, prefix, 0, 1, SubSize - 1) == 0; }
      
      





同様に、静的な行を終了する場合:







 template<size_t SubSize, size_t Size> constexpr bool static_string_ends_with(const static_string<Size>& str, const static_string<SubSize>& suffix) { return SubSize > Size ? false : static_string_compare(str, Size - SubSize, suffix, 0, 1, SubSize - 1) == 0; }
      
      





静的な文字列部分文字列を使用する



ここでは、静的文字列の部分文字列に関連付けられた操作を見てみましょう。







静的文字列の部分文字列、接頭辞、および接尾辞の取得



前述したように、部分文字列を取得するには、指定された開始インデックスと終了インデックスを使用して、一連のインデックスを生成する必要があります。







 template<size_t Begin, size_t End, size_t ... Indexes> struct make_index_subsequence : make_index_subsequence<Begin, End - 1, End - 1, Indexes ...> {}; template<size_t Pos, size_t ... Indexes> struct make_index_subsequence<Pos, Pos, Indexes ...> : index_sequence<Indexes ...> {};
      
      





static_assert



を使用して部分文字列の開始と終了をチェックして、部分文字列の取得を実装します。







 template<size_t Begin, size_t End, size_t Size> constexpr auto static_string_substring(const static_string<Size>& str) { static_assert(Begin <= End, "Begin is greater than End (Begin > End)"); static_assert(End <= Size - 1, "End is greater than string length (End > Size - 1)"); return make_static_string(str, make_index_subsequence<Begin, End>{}); }
      
      





接頭辞は、元の静的文字列の先頭と一致する部分文字列です。







 template<size_t End, size_t Size> constexpr auto static_string_prefix(const static_string<Size>& str) { return static_string_substring<0, End>(str); }
      
      





同様に、接尾辞についても、末尾のみが一致します。







 template<size_t Begin, size_t Size> constexpr auto static_string_suffix(const static_string<Size>& str) { return static_string_substring<Begin, Size - 1>(str); }
      
      





特定のインデックスで静的文字列を2つに分割します



特定のインデックスで静的な文字列を分割するには、プレフィックスとサフィックスを返すだけで十分です。







 template<size_t Index, size_t Size> constexpr auto static_string_split(const static_string<Size>& str) { return std::make_pair(static_string_prefix<Index>(str), static_string_suffix<Index + 1>(str)); }
      
      





静的ストリング反転



静的な文字列を逆にするには、逆の順序でインデックスを生成するインデックスジェネレータを作成します。







 template<size_t Size, size_t ... Indexes> struct make_reverse_index_sequence : make_reverse_index_sequence<Size - 1, Indexes ..., Size - 1> {}; template<size_t ... Indexes> struct make_reverse_index_sequence<0, Indexes ...> : index_sequence<Indexes ...> {};
      
      





次に、静的文字列を逆にする関数を実装します。







 template<size_t Size> constexpr auto static_string_reverse(const static_string<Size>& str) { return make_static_string(str, make_reverse_index_sequence<Size - 1>{}); }
      
      





静的文字列ハッシュ計算



次の式を使用してハッシュを計算します。







H(s)=(s 0 + 1)⋅33 0 +(s 1 + 1)⋅33 1 + ... +(s n-1 + 1)⋅33 n-1 + 5381⋅33 n mod 2 64







 template<size_t Size> constexpr unsigned long long static_string_hash(const static_string<Size>& str, size_t index) { return index >= Size - 1 ? 5381ULL : static_string_hash(str, index + 1) * 33ULL + str[index] + 1; }
      
      





数値を静的文字列に、またはその逆に変換します



この章では、静的文字列から整数への変換、およびその逆について説明します。 簡単にするために、数値はlong long



型とunsigned long long



型で表されると仮定します。これらは大容量の型であり、ほとんどの場合に適しています。







数値を静的な文字列に変換します



数値を静的な文字列に変換するには、数値のすべての数字を取得し、対応する文字に変換して、これらの文字の文字列を作成する必要があります。







数値のすべての数字を取得するには、インデックスシーケンスジェネレーターに似たジェネレーターを使用します。 文字のシーケンスを定義します。







 template<char ... Chars> struct char_sequence {};
      
      





数字文字ジェネレーターを実装し、最初のパラメーターに現在の数字を保存し、次の数字を保存します。次の数字がシーケンスの先頭に追加され、数字が10で除算されます。







 template<unsigned long long Value, char ... Chars> struct make_unsigned_int_char_sequence : make_unsigned_int_char_sequence<Value / 10, '0' + Value % 10, Chars ...> {};
      
      





現在の数値が0の場合、それを破棄して数字のシーケンスを返します。変換するものはもうありません。







 template<char ... Chars> struct make_unsigned_int_char_sequence<0, Chars ...> : char_sequence<Chars ...> {};
      
      





初期数がゼロの場合も考慮する必要があります。この場合、ヌル文字を返す必要があります。そうでない場合、ゼロは空の文字列に変換され、空の文字列に変換されます。







 template<> struct make_unsigned_int_char_sequence<0> : char_sequence<'0'> {};
      
      





実装されたジェネレーターは、正の数には最適ですが、負の数には適していません。 最初にテンプレートパラメータをもう1つ追加して、新しいジェネレーターを定義します-変換された数値の符号:







 template<bool Negative, long long Value, char ... Chars> struct make_signed_int_char_sequence {};
      
      





上記のように番号を処理しますが、記号を考慮します。







 template<long long Value, char ... Chars> struct make_signed_int_char_sequence<true, Value, Chars ...> : make_signed_int_char_sequence<true, Value / 10, '0' + -(Value % 10), Chars ...> {}; template<long long Value, char ... Chars> struct make_signed_int_char_sequence<false, Value, Chars ...> : make_signed_int_char_sequence<false, Value / 10, '0' + Value % 10, Chars ...> {};
      
      





ここには微妙な点が1つあります。- -(Value % 10)



注意してください。 ここでは、負の数値の範囲は正の数値の範囲よりも1つの数値が大きく、最小数値のモジュラスは有効な値のセットから外れるため、 -Value % 10



は使用できません。







処理後に数値を破棄し、負の場合はマイナス記号を追加します。







 template<char ... Chars> struct make_signed_int_char_sequence<true, 0, Chars ...> : char_sequence<'-', Chars ...> {}; template<char ... Chars> struct make_signed_int_char_sequence<false, 0, Chars ...> : char_sequence<Chars ...> {};
      
      





それとは別に、ゼロの変換を処理します。







 template<> struct make_signed_int_char_sequence<false, 0> : char_sequence<'0'> {};
      
      





最後に、変換関数を実装します。







 template<unsigned long long Value> constexpr auto uint_to_static_string() { return make_static_string(make_unsigned_int_char_sequence<Value>{}); } template<long long Value> constexpr auto int_to_static_string() { return make_static_string(make_signed_int_char_sequence<(Value < 0), Value>{}); }
      
      





静的文字列を数値に変換します



静的文字列を数値に変換するには、文字を数値に変換してから、10を対応する累乗で乗算してから追加する必要があります。 すべてのアクションを再帰的に実行し、空の文字列に対してはゼロを返します。







 template<size_t Size> constexpr unsigned long long static_string_to_uint(const static_string<Size>& str, size_t index) { return Size < 2 || index >= Size - 1 ? 0 : (str[index] - '0') + 10ULL * static_string_to_uint(str, index - 1); } template<size_t Size> constexpr unsigned long long static_string_to_uint(const static_string<Size>& str) { return static_string_to_uint(str, Size - 2); }
      
      





符号付きの数値を変換するには、負の数値がマイナス記号で始まることを考慮する必要があります。







 template<size_t Size> constexpr long long static_string_to_int(const static_string<Size>& str, size_t index, size_t first) { return index < first || index >= Size - 1 ? 0 : first == 0 ? (str[index] - '0') + 10LL * static_string_to_int(str, index - 1, first) : -(str[index] - '0') + 10LL * static_string_to_int(str, index - 1, first); } template<size_t Size> constexpr long long static_string_to_int(const static_string<Size>& str) { return Size < 2 ? 0 : str[0] == '-' ? static_string_to_int(str, Size - 2, 1) : static_string_to_int(str, Size - 2, 0); }
      
      





ライブラリの使いやすさに関する考慮事項



この時点で、ライブラリを完全に使用することはすでに可能ですが、いくつかの点は不便です。 この章では、ライブラリをより便利に使用する方法について説明します。







静的行オブジェクト



文字列と実装されたメソッドをオブジェクトにパックします。 これにより、比較演算子を実装するだけでなく、より短いメソッド名を使用できます。







 template<size_t Size> struct static_string { constexpr size_t length() const { return Size - 1; } constexpr size_t size() const { return Size; } constexpr size_t begin() const { return 0; } constexpr size_t end() const { return Size - 1; } constexpr size_t rbegin() const { return Size - 2; } constexpr size_t rend() const { return std::numeric_limits<size_t>::max(); } constexpr bool empty() const { return Size < 2; } constexpr auto reverse() const { return static_string_reverse(*this); } template<size_t Begin, size_t End> constexpr auto substring() const { return static_string_substring<Begin, End>(*this); } template<size_t End> constexpr auto prefix() const { return static_string_prefix<End>(*this); } template<size_t Begin> constexpr auto suffix() const { return static_string_suffix<Begin>(*this); } constexpr size_t find(char ch, size_t from = 0, size_t nth = 0) const { return static_string_find(*this, ch, from, nth); } template<size_t SubSize> constexpr size_t find(const static_string<SubSize>& substr, size_t from = 0, size_t nth = 0) const { return static_string_find(*this, substr, from, nth); } template<size_t SubSize> constexpr size_t find(const char (& substr)[SubSize], size_t from = 0, size_t nth = 0) const { return static_string_find(*this, substr, from, nth); } constexpr size_t rfind(char ch, size_t from = Size - 2, size_t nth = 0) const { return static_string_rfind(*this, ch, from, nth); } template<size_t SubSize> constexpr size_t rfind(const static_string<SubSize>& substr, size_t from = Size - SubSize, size_t nth = 0) const { return static_string_rfind(*this, substr, from, nth); } template<size_t SubSize> constexpr size_t rfind(const char (& substr)[SubSize], size_t from = Size - SubSize, size_t nth = 0) const { return static_string_rfind(*this, substr, from, nth); } constexpr bool contains(char ch) const { return static_string_contains(*this, ch); } template<size_t SubSize> constexpr bool contains(const static_string<SubSize>& substr) const { return static_string_contains(*this, substr); } template<size_t SubSize> constexpr bool contains(const char (& substr)[SubSize]) const { return static_string_contains(*this, substr); } template<size_t SubSize> constexpr bool starts_with(const static_string<SubSize>& prefix) const { return static_string_starts_with(*this, prefix); } template<size_t SubSize> constexpr bool starts_with(const char (& prefix)[SubSize]) const { return static_string_starts_with(*this, prefix); } template<size_t SubSize> constexpr bool ends_with(const static_string<SubSize>& suffix) const { return static_string_ends_with(*this, suffix); } template<size_t SubSize> constexpr bool ends_with(const char (& suffix)[SubSize]) const { return static_string_ends_with(*this, suffix); } constexpr size_t count(char ch) const { return static_string_count(*this, ch); } template<size_t Index> constexpr auto split() const { return static_string_split<Index>(*this); } constexpr unsigned long long hash() const { return static_string_hash(*this); } constexpr char operator[](size_t index) const { return data[index]; } std::string str() const { return to_string(*this); } std::array<const char, Size> data; };
      
      







. :







 template<size_t Size1, size_t Size2> constexpr bool operator<(const static_string<Size1>& str1, const static_string<Size2>& str2) { return static_string_compare(str1, str2) < 0; }
      
      





> <= >= == !=, . - .









:







 #define ITOSS(x) int_to_static_string<(x)>() #define UTOSS(x) uint_to_static_string<(x)>() #define SSTOI(x) static_string_to_int((x)) #define SSTOU(x) static_string_to_uint((x))
      
      







.







:







 constexpr auto hello = make_static_string("Hello"); constexpr auto world = make_static_string("World"); constexpr auto greeting = hello + ", " + world + "!"; // greeting == "Hello, World!"
      
      





, :







 constexpr int apples = 5; constexpr int oranges = 7; constexpr auto message = static_string_concat("I have ", ITOSS(apples), " apples and ", ITOSS(oranges), ", so I have ", ITOSS(apples + oranges), " fruits"); // message = "I have 5 apples and 7 oranges, so I have 12 fruits"
      
      





 constexpr unsigned long long width = 123456789ULL; constexpr unsigned long long height = 987654321ULL; constexpr auto message = static_string_concat("A rectangle with width ", UTOSS(width), " and height ", UTOSS(height), " has area ", UTOSS(width * height)); // message = "A rectangle with width 123456789 and height 987654321 has area 121932631112635269"
      
      





 constexpr long long revenue = 1'000'000LL; constexpr long long costs = 1'200'000LL; constexpr long long profit = revenue - costs; constexpr auto message = static_string_concat("The first quarter has ended with net ", (profit >= 0 ? "profit" : "loss "), " of $", ITOSS(profit < 0 ? -profit : profit)); // message == "The first quarter has ended with net loss of $200000"
      
      





URL:







 constexpr auto url = make_static_string("http://www.server.com:8080"); constexpr auto p = url.find("://"); constexpr auto protocol = url.prefix<p>(); // protocol == "http" constexpr auto sockaddr = url.suffix<p + 3>(); constexpr auto hp = sockaddr.split<sockaddr.find(':')>(); constexpr auto host = hp.first; // host == "www.server.com" constexpr int port = SSTOI(hp.second); // port == 8080
      
      





:







 constexpr auto str = make_static_string("Hello"); for (size_t i = str.begin(); i != str.end(); ++i) //  std::cout << str[i]; std::cout << std::endl; // Hello for (size_t i = str.rbegin(); i != str.rend(); --i) //  std::cout << str[i]; std::cout << std::endl; // olleH
      
      





参照資料



, , github







, .







更新する



_ss :







 template<typename Char, Char ... Chars> constexpr basic_static_string<Char, sizeof ... (Chars) + 1> operator"" _ss() { return {Chars ..., static_cast<Char>('\0')}; };
      
      





make_static_string() , :







 constexpr auto hello_world = "Hello"_ss + " World"; if ("Hello" < "World"_ss) { ... } constexpr auto hash = "VeryLongString"_ss.hash();
      
      





Char char:







 template<typename Char, size_t Size> struct basic_static_string { // ... std::array<const Char, Size> data; };
      
      





char whar_t, , concat, :







 template<size_t Size> using static_string_t = basic_static_string<char, Size>; template<size_t Size> using static_wstring_t = basic_static_string<wchar_t, Size>; using static_string = basic_static_string<char, 0>; using static_wstring = basic_static_string<wchar_t, 0>;
      
      





"" :







 constexpr auto wide_string = L"WideString"_ss; constexpr int apples = 5; constexpr int oranges = 7; constexpr int fruits = apples + oranges; constexpr auto str3 = static_wstring::concat(L"I have ", ITOSW(apples), L" apples and ", ITOSW(oranges), L" oranges, so I have ", ITOSW(fruits), L" fruits"); static_assert(str3 == L"I have 5 apples and 7 oranges, so I have 12 fruits", ""); std::wcout << str3 << std::endl;
      
      





size(), size() length() , sizeof():







 constexpr auto ss1 = "Hello"_ss; static_assert(ss1.length() == 5, ""); static_assert(ss1.size() == 5, ""); static_assert(sizeof(ss1) == 6, "");
      
      





github

.







更新2



AndreySuとの議論の間に、文字がテンプレートパラメータとして渡される静的な文字列を実装する別の方法がありました。







 #include <iostream> using namespace std; template<typename Char, Char ... Chars> struct static_string{}; template<typename Char, Char ... Chars1, Char ... Chars2> constexpr static_string<Char, Chars1 ..., Chars2 ... > operator+( const static_string<Char, Chars1 ... >& str1, const static_string<Char, Chars2 ... >& str2) { return static_string<Char, Chars1 ..., Chars2 ...>{}; } template<typename Char, Char ch, Char ... Chars> std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& bos, const static_string<Char, ch, Chars ...>& str) { bos << ch << static_string<Char, Chars ... >{}; return bos; } template<typename Char> std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& bos, const static_string<Char>& str) { return bos; } template<typename Char, Char ... Chars> constexpr static_string<Char, Chars ... > operator"" _ss() { return static_string<Char, Chars ... >{}; }; int main() { constexpr auto str1 = "abc"_ss; constexpr auto str2 = "def"_ss; constexpr auto str = str1 + str2 + str1; std::cout << str << std::endl; return 0; }
      
      





このオプションの詳細な検討は記事の範囲を超えており、読者の一人がそれを思い起こさせることができればうれしいです。








All Articles