C++でC風ライブラリを作る(商と剰余編2)
高木です。こんばんは。
前回は、商と剰余を求めるdiv
関数のうち、2つの引数、numer
とdenom
が同じ型のものを作成しました。
しかし、これだけでは不便です。
実引数の型が異なれば、多重定義が解決できずにコンパイルエラーになってしまうからです。
今回は、2つの実引数の型が異なる場合にも使うことができるudiv
関数を設計&実装してみたいと思います。
udiv
関数はその名の通り、商と剰余を符合無し整数型で返します。
しかし、2つの実引数を単純に符合無し整数型にそろえてから演算を行うだけでは面白くありません。
結果の符合についても何らかの形で返すことにします。
ところで、2つの実引数が異なる場合でも、両方が符合付き整数型または符合無し整数型の場合は前回のdiv
関数を使えますので、今回はあくまでも符合の有無が異なる引数が混在する場合について考えます。
もちろん、udiv
関数に同じ型の実引数を渡せないようでは不便なので、こちらも合わせて考えていくことにしましょう。
int
およびunsigned int
型の組み合わせだけを考えても次の4種類があることになります。
int
とint
unsigned int
とunsigned int
int
とunsigned int
unsigned int
とint
サイズが異なる型、すなわちlong
やlong long
についても同じことがいえます。
次に返却値の型を考えることにしましょう。
結果を符合無し整数型で返すことは前述の通りですが、引数に符合付き整数型が混在した場合、結果にも符合が必要になることは間違いありません。
単純に、
1 2 3 4 5 6 7 8 | template <typename T> struct udiv_t { T quot; T rem; bool quot_sign; bool rem_sign; }; |
のようにすることもできますが、これでは符合の存在を忘れるミスを誘発してしまいます。
いろいろ考え方はあると思いますが、今回は次のように、quot
およびrem
の型を工夫することで極力ミスを軽減することにしました。
1 2 3 4 5 6 7 8 9 10 11 12 | namespace cloverfield { template <typename T> struct udiv_t { struct { T abs; bool sign; } quot, rem; }; } |
ここまで方針が決まれば、あとは機械的な作業になります。
まずは、共通のヘルパー関数を作ることにしましょう。
被除数と除数の絶対値と符合を受け取って結果を求める関数です。
型が違ってもやることは同じですのでテンプレートにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | namespace cloverfield { namespace detail { template <typename T> constexpr udiv_t<T> udiv_helper(T numer, bool numer_sign, T denom, bool denom_sign) { return { { denom == 0 ? (throw std::invalid_argument("cloverfield::udiv"), 0) : numer / denom, numer_sign ^ denom_sign }, { numer % denom, numer_sign } }; } } } |
あとは、このヘルパー関数を呼び出すudiv
関数を型ごとに定義すれば完成です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | namespace cloverfield { constexpr udiv_t<unsigned int> udiv(unsigned int numer, unsigned int denom) { return detail::udiv_helper(numer, false, denom, false); } constexpr udiv_t<unsigned int> udiv(int numer, int denom) { return detail::udiv_helper(uabs(numer), numer < 0, uabs(denom), denom < 0); } constexpr udiv_t<unsigned int> udiv(int numer, unsigned int denom) { return detail::udiv_helper(uabs(numer), numer < 0, denom, false); } constexpr udiv_t<unsigned int> udiv(unsigned int numer, int denom) { return detail::udiv_helper(numer, false, uabs(denom), denom < 0); } } |
今回は符合付き整数型どうしであってもオーバーフローが発生することはありませんので、エラーチェックはゼロ除算だけにしています。