大阪市中央区 システムソフトウェア開発会社

営業時間:平日09:15〜18:15
MENU

C++でC風ライブラリを作る(文字列長編)

著者:高木信尚
公開日:2019/05/26
最終更新日:2019/05/26
カテゴリー:技術情報

高木です。こんばんは。

深夜ですが、日曜日は終日出かける予定なので今のうちに投稿しておくことにします。
今回のテーマは文字列の長さです。

標準Cライブラリには文字列の長さを求める関数として、strlen関数とwcslen関数が用意されています。
また、標準C++ライブラリにはstd::basic_stringクラステンプレートのlengthおよびsizeメンバ関数、それからstd::char_traitsクラステンプレートのlengthメンバ関数も備わっています。

用途ごとに関数は用意されているのですが、関数名がいろいろで統一感がありません。
こういう記憶力に負荷をかける仕様は私にとっては辛いので、すべてを統一的に利用できるようにライブラリを設計したいと思います。

まずは一番基本となる文字型へのポインタを渡して文字列の長さ、すなわち先頭からナル文字までの距離を求める関数です。
これにはstd::char_traitsクラステンプレートのlengthメンバ関数を用いることにします。

namespace cloverfield
{
  template <typename charT>
  inline std::size_t strlen(charT const* s)
  {
    return std::char_traits<charT>::length(s);
  }
}

ところで、char型とwchar_t型については、それぞれ専用の関数を用いる方が処理系が上手く最適化してくれているかもしれません。
念のため、それらの多重定義を行っておくことにしましょう。

namespace cloverfield
{
  inline std::size_t strlen(char const* s)
  {
    return std::strlen(s);
  }

  inline std::size_t strlen(wchar_t const* s)
  {
    return std::wcslen(s);
  }
}

ところでPOSIXにはstrnlen関数というのがあります。
範囲指定がまったくないままナル文字を探索するのは怖いので、こういう関数があるのでしょう。
事前に範囲が分かっているのであれば、探索範囲の上限を指定できたほうがいいでしょうね。

namespace cloverfield
{
  template <typename charT>
  inline std::size_t strlen(charT const* s, std::size_t n)
  {
    return std::find(s, s + n, charT(0)) - s;
  }

  inline std::size_t strlen(char const* s, std::size_t n)
  {
    if (auto ss = static_cast<char const*>(std::memchr(s, '\0', n)))
      return ss - s;
    return n;
  }

  inline std::size_t strlen(wchar_t const* s, std::size_t n)
  {
    if (auto ss = static_cast<wchar_t const*>(std::wmemchr(s, L'\0', n)))
      return ss - s;
    return n;
  }
}

最後にstd::basic_stringを引数に取る場合も多重定義します。

namespace cloverfield
{
  template <typename charT, typename traits, typename Allocator>
  inline std::size_t strlen(std::basic_string<charT, traits, Allocator> const& s)
  {
    return s.length();
  }

  template <typename charT, typename traits, typename Allocator>
  inline std::size_t strlen(std::basic_string<charT, traits, Allocator> const& s, std::size_t n)
  {
    return std::min(s.length(), n);
  }
}

これで一通り完成なのですが、ひとつだけ心残りがあります。
引数に配列を指定した場合、配列の要素数を自動的にナル文字の探索範囲にできなかったことです。
これについては次回にもう少し考えてみることにします。

    上に戻る