constexpr な strtol 関数を書いた
書きました。
constexpr だと代入が出来ないので、第二引数が NULL、もしくは引数が文字列と基数の場合のみ constexpr な値を返します。
[ソース]
#include <cstdlib> #include <climits> namespace ce{ // 暫定版 // 英字の連続性は保証されていないので、ぐぬぬ。 template<typename Char> constexpr bool isdigit(Char c){ return Char('0') <= c && c <= Char('9'); } template<typename Char> constexpr bool isupper(Char c){ return Char('A') <= c && c <= Char('Z'); } template<typename Char> constexpr bool islower(Char c){ return Char('a') <= c && c <= Char('z'); } template<typename Char> constexpr long ctoi(Char c){ return isdigit(c) ? c - Char('0') : islower(c) ? 10 + (c - Char('a')) : isupper(c) ? 10 + (c - Char('A')) : Char(-1); } template<typename Char> constexpr long strtol_impl(Char const* s, int radix, int sign, long tmp){ return ctoi(s[0]) == -1 || ctoi(s[0]) >= radix ? tmp : tmp > (LONG_MAX - ctoi(s[0]) - sign) / radix ? (sign ? LONG_MIN : LONG_MAX) : s[1] == Char('\0') ? tmp * radix + ctoi(s[0]) : strtol_impl(s + 1, radix, sign, tmp * radix + ctoi(s[0])); } template<typename Char> constexpr long strtol_prefix(Char const* s, int radix, int sign){ return s[0] != Char('0') ? strtol_impl(s, radix == 0 ? 10 : radix, sign, 0) : s[1] == Char('x') || s[1] == Char('X') ? strtol_impl(s + 2, radix == 0 ? 16 : radix, sign, 0) : strtol_impl(s + 1, radix == 0 ? 8 : radix, sign, 0); } template<typename Char> constexpr long strtol_sign(Char const* s, int radix){ return s[0] == Char('-') ? -strtol_prefix(s+1, radix, 1) : s[0] == Char('+') ? strtol_prefix(s+1, radix, 0) : strtol_prefix(s, radix, 0); } template<typename Char> constexpr long strtol_removespace(Char const* s, int radix){ return s[0] == Char(' ') ? strtol_removespace(s+1, radix) : strtol_sign(s, radix); } template<typename Char> constexpr long strtol(Char const* s, int radix){ return strtol_removespace(s, radix); } template<typename Char> constexpr long strtol(Char const* s, char** endptr, int radix){ return endptr == NULL ? strtol(s, radix) : std::strtol(s, endptr, radix); } } // namespace ce #include <iostream> int main(){ /* static_assert(ce::isdigit('0'), ""); static_assert(ce::isdigit('9'), ""); static_assert(ce::isdigit('5'), ""); static_assert(!ce::isdigit('a'), ""); static_assert(ce::isupper('V'), ""); static_assert(!ce::isupper('a'), ""); static_assert(!ce::isupper('0'), ""); static_assert(ce::strtol("52", 0) == 52, ""); static_assert(ce::strtol("0377", 0) == 255, ""); static_assert(ce::strtol("0xff", 0) == 255, ""); static_assert(ce::strtol("0xFF", 0) == 255, ""); */ static_assert(ce::strtol("52", 10) == 52, ""); static_assert(ce::strtol("377", 8) == 255, ""); static_assert(ce::strtol("1001", 2) == 9, ""); static_assert(ce::strtol("ff", 16) == 255, ""); static_assert(ce::strtol(" +42", 10) == 42, ""); static_assert(ce::strtol(" -42", 10) == -42, ""); static_assert(ce::strtol("-10hoge", 0) == -10, ""); static_assert(ce::strtol("f3fa", 0) == 0, ""); static_assert(ce::strtol("+0xff", 16) == 255, ""); static_assert(ce::strtol("3087663490", 10) == LONG_MAX, ""); static_assert(ce::strtol("1234", 5) == 194, ""); static_assert(ce::strtol("ABCDEF", 0) == 0, ""); // std::strtol が呼ばれる char str[] = "42fc"; char* error = NULL; std::cout << ce::strtol(str, &error, 10) << std::endl; std::cout << error << std::endl; return 0; }
[出力]
42 fc
おお、だいぶ酷いソースに…もっと綺麗にしたい。
本当は英字の連続性は規格で保障されていないので、そこら辺の処理をどうにかしないとダメなんですが、今回はあまり深く考えないで書いて見ました。
あとテストが適当なのでバグがあるかも。
しかし、constexpr だとソースを綺麗にするのが大変ですね。
ローカル変数が使えないので、変な関数が増えていく…。
あと条件演算子も慣れてないと見づらい。
まぁ constexpr な関数を作っている時は縛りプレイみたいな感じで結構楽しかったりするんですが。