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

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

C++でC風ライブラリを作る(文字列→整数変換編2)

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

高木です。こんばんは。

前回予告したように、今回は文字列から符合無し整数への変換を完成させることにします。
前回使ったtolower関数もそうなのですが、今回も以前、文字種別編で作った関数を有効活用していきます。
ただし、完成とはいっても、ボリュームの関係でエラー処理は次回まわしにしたいと思います。

それでは早速実装に入っていきます。
まずは、先行する空白類文字を読み飛ばし、符合を解釈するためのヘルパー関数space_signから作っていきましょう。

namespace cloverfield
{
  namespace detail
  {
    template <typename Iterator>
    bool space_sign(Iterator& next, Iterator last)
    {
      using char_type = typename std::iterator_traits<Iterator>::value_type;
      bool sign = false;

      for (; next != last; ++next)
      {
        switch (auto c = *next)
        {
        case char_type('-'):
          sign = true;
        case char_type('+'):
          ++next;
          return sign;
        default:
          if (!isspace(c))
            return false;
          break;
        }
      }
      return false;
    }
  }
}

次に、Radixに0を指定した場合は接頭辞を解析して基数を設定する必要がありますので、これもヘルパー関数を用意しましょう。

namespace cloverfield
{
  namespce detail
  {
    template <typename Iterator>
    int radix_prefix(Iterator& next, Iterator last)
    {
      using char_type = typename std::iterator_traits<Iterator>::value_type;
      if (next != last)
      {
        if (*next == char_type('0'))
        {
          if (++next != last)
          {
            switch (*next)
            {
            case char_type('b'):
              ++next;
              return 2;
            case char_type('x'):
              ++next;
              return 16;
            default:
              break;
            }
          }
          return 8;
        }
      }
      return 10;
    }
  }
}

そして、これらを使ってstrto関数テンプレートを実装します。

namespace cloverfield
{
  template <typename Type, int Radix, typename Iterator>
  Type strto(Iterator first, Iterator last, Iterator* endit)
  {
    bool sign = detail::space_sign(first, last);
    Type u;

    if (Radix == 0)
    {
      switch (detail::radix_prefix(first, last))
      {
      case 2:
        u = detail::strto_helper<Type, 2>(first, last, endit);
        break;
      case 8:
        u = detail::strto_helper<Type, 8>(first, last, endit);
        break;
      case 16:
        u = detail::strto_helper<Type, 16>(first, last, endit);
        break;
      default:
        u = detail::strto_helper<Type, 10>(first, last, endit);
        break;
      }
    }
    else
    {
      u = detail::strto_helper<Type, Radix>(first, last, endit);
    }
    return sign? -u : u;
  }
}

前回、strto_helper関数テンプレート内で、static_assertを用いて基数を2~36に制限しましたが、これをやるとstrto関数テンプレートにRadixを0にして呼び出す際にエラーになってしまいますので、いったん除去することにします。
必要なら、0も許可する形にしないといけませんね。

今回で完成まで持っていこうと思ったのですが、ちょっと長くなりすぎました。
次回もう1回だけ時間を取って、最後まで完成させたいと思います。

残作業としては、利便性を向上させるためにいくつかのバリエーションを多重定義することと、エラー処理です。
オーバーフローが発生した場合の処理をもう少し頑張りたいと思います。

    上に戻る