C++でC風ライブラリを作る(文字探索編)
高木です。こんにちは。
文字列処理関数が続いています。
こうやって見ると結構な量がありますね。
地道な作業ですが、徹底的にやりたいと思います(ブログのネタにも困りませんからね)。
今回は文字探索関数です。
Cの文字列中の文字探索関数には、大きく分けて2種類あります。
ひとつは、文字列の先頭から末尾に向けて探索するstrchr
関数です。
もうひとつは、逆に文字列の末尾から文字列の先頭に向けて探索する関数です。
strchr
関数やstrrchr
関数はchar
型の文字列のためのものですが、wchar_t
型の文字列のためのwcschr
関数とwcsrchr
関数もあります。
今回のライブラリでは、いつものようにchar16_t
型などの文字列にも対応します。
Cのstrchr
関数やstrrchr
関数はchar const*
型を受け取りchar*
型を返します。
多重定義が可能なC++では、char const*
型を受け取りchar const*
を返すものと、char*
型を受け取りchar*
を返すものが用意されています。
第2引数はint
型ですが、昔は関数原型(プロトタイプ)無しでも呼び出せる必要があったからではないかと思います。
今となっては、またC++では意味がないので、素直にchar
型を受け取るようにしたほうがいいでしょう。
getc
関数などint
型を返す関数との相性を考えるとint
型にするのも悪くないんでしょうけど。
それでは実装を見ていきましょう。
まずは素直にテンプレートを使って実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | namespace cloverfield { template <typename charT> inline charT* strchr(charT* s, charT c) { while (auto cc = *s) { if (cc == c) return s; ++s; } return nullptr; } template <typename charT> inline charT const* strchr(charT const* s, charT c) { return strchr(const_cast<charT*>(s), c); } } |
そして、char
型やwchar_t
型の文字列のための関数を多重定義します。
これもいつも通りですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | namespace cloverfield { inline char* strchr(char* s, char c) { return std::strchr(s, c); } inline char const* strchr(char const* s, char c) { return std::strchr(s, c); } inline wchar_t* strchr(wchar_t* s, wchar_t c) { return std::wcschr(s, c); } inline wchar_t const* strchr(wchar_t const* s, wchar_t c) { return std::wcschr(s, c); } } |
最後にstd::basic_string
クラステンプレートのための関数を定義しましょう。
1 2 3 4 5 6 7 8 | namespace cloverfield { template <typename charT, typename traits, typename Allocator> inline charT const* strchr(std::basic_string<charT, traits, Allocator> const& s, charT c) { return strchr(s.c_str(), c); } } |
このライブラリはC++11を使うことにしているので、std::basic_string
クラステンプレート版はconst
修飾されていない文字列を返す版はあえて定義していません。
C++17では、const
修飾のないポインタを返すdata
メンバ関数もあるのですが、C++11の範囲では無理矢理const_cast
でconst
修飾を外さないといけませんし、そうしたポインタを介して文字を上書きすると未定義の動作になるのでやめておきます。
次回はstrrchr
関数を定義することにします。
ほとんど変わらないんですけどね。