constexpr atof

書きました。
一応、文字列の最後の位置も返すようにしたのですが、strtod と比べて挙動が違ってしまっているので、ぐぬぬ…。
ここら辺は一度、設計を見直す必要がありますねぇ(´・ω・`)
文字列の位置を保持するのは難しい…。

[ソース]

#include <cstdlib>
#include <iostream>

namespace ce{

template<typename Char>
constexpr int
ctoi(Char c){
    return Char('0') <= c && c <= Char('9')
        ? c - Char('0')
        : Char(-1);
}

template<typename T, typename Char>
struct with_c_str{
    constexpr with_c_str(T value, Char const* str)
        : value(value), str(str){}
    constexpr operator T() const{
        return value;
    }
    constexpr T get() const{
        return value;
    }
    constexpr Char const* c_str() const{
        return str;
    }
private:
    T value;
    Char const* str;
};


template<typename T, typename Char>
constexpr with_c_str<T, Char>
minus(with_c_str<T, Char> const& value){
    return with_c_str<T, Char>(value * -1, value.c_str());
}

template<typename T, typename Char>
constexpr with_c_str<T, Char>
operator *(T t, with_c_str<T, Char> const& value){
    return with_c_str<T, Char>(t * value.get(), value.c_str());
}

template<typename T, typename Char>
constexpr with_c_str<T, Char>
operator /(T t, with_c_str<T, Char> const& value){
    return with_c_str<T, Char>(t / value.get(), value.c_str());
}

template<typename Char>
constexpr Char const*
remove_space(Char const* s){
    return s[0] != Char(' ') ? s
         : remove_space(s+1);
}

template<typename Char>
constexpr bool
isdigit(Char c){
    return Char('0') <= c && c <= Char('9');
}

template<typename Char, typename Result = with_c_str<int, Char>>
constexpr Result
atoi_impl(Char const* s, int tmp){
    return ctoi(s[0]) == -1 ? Result(tmp, s)
         : atoi_impl(s + 1, tmp * 10 + ctoi(s[0]));
}

template<typename Char, typename Result = with_c_str<int, Char>>
constexpr Result
atoi_sign(Char const* s){
    return s[0] != Char('-') ? atoi_impl(s, 0)
         : isdigit(s[1])     ? minus(atoi_impl(s + 1, 0))
         : Result(0, s);
}

template<typename Char>
constexpr auto
atoi(Char const* s)->decltype(atoi_sign(remove_space(s))){
    return atoi_sign(remove_space(s));
}

template<typename Char>
constexpr bool
equal(Char const* a, Char const* b){
    return a[0] == Char('\0') && b[0] == Char('\0') ? true
         : a[0] == b[0]                             ? equal(a+1, b+1)
         : false;
}

constexpr double
pow(double x, unsigned int y){
    return y <= 1 ? x
         : x * pow(x, y-1);
}

constexpr int
keta(int n, int sum = 0){
    return n <= 0 ? sum
         : keta(n/10, sum+1);
}

template<typename T, typename Char, typename Result = with_c_str<double, Char>>
constexpr Result
atof_power_impl(with_c_str<T, Char> const& n){
    return n == 0 ? Result(1, n.c_str())
         : n == 0 ? Result(1, n.c_str())
         : n >  0 ? Result(pow(10.0, n), n.c_str())
         : Result(1 / pow(10.0, n*-1), n.c_str());
}

template<typename Char>
constexpr bool
isdigit_plus_minus(Char c){
    return (Char('0') <= c && c <= Char('9'))
        || c == Char('-')
        || c == Char('+');
}

template<typename Char, typename Result = with_c_str<double, Char>>
constexpr Result
atof_power(Char const* s){
    return (s[0] == Char('e') || s[0] == Char('E')) && atoi(s+1).c_str() != (s+1)
         ? atof_power_impl(atoi(s+1))
         : Result(1, s);
}

template<typename T, typename Char, typename Result = with_c_str<double, Char>>
constexpr Result
atof_impl(with_c_str<T, Char> const& n, double sum){
    return (n / pow(10.0, keta(n)) + sum) * atof_power(n.c_str());
}

template<typename Char, typename Result = with_c_str<double, Char>>
constexpr Result
atof_dot(Char const* s, int sum){
    return s[0] == Char('.') ? atof_impl(atoi(s+1), sum)
         : Result(sum, s);
}

template<typename T, typename Char, typename Result = with_c_str<double, Char>>
constexpr Result
atof(with_c_str<T, Char> const& atoi){
    return atoi < 0
         ? minus(atof_dot(atoi.c_str(), -atoi))
         : atof_dot(atoi.c_str(),  atoi);
}

template<typename Char>
constexpr auto
atof(Char const* s)->decltype(atof(atoi(s))){
    return atof(atoi(s));
}

}  // namespace ce

#include <cstdlib>

int
main(){
    static_assert(ce::atof("53.132")    == 53.132, "");
    static_assert(ce::atof("42")        == 42, "");
    static_assert(ce::atof("0.14035")   == 0.14035, "");
    static_assert(ce::atof("")          == 0, "");
    static_assert(ce::atof(".")         == 0, "");
    static_assert(ce::atof("3...14")    == 3, "");
    static_assert(ce::atof("    -3.14") == -3.14, "");
    static_assert(ce::atof("0.25e2")    == 25, "");
    static_assert(ce::atof("24.25E4")   == 242500, "");
    static_assert(ce::atof("0.25e2")    == 25, "");
    static_assert(ce::atof("0.4e-2")    == 0.004, "");

    static constexpr auto result = ce::atof("24.25EE4");
    static_assert(result == 24.25, "");
    static_assert(ce::equal(result.c_str(), "EE4"), "");

    static constexpr auto result2 = ce::atof("0.25e-");
    static_assert(result2 == 0.25, "");
    static_assert(ce::equal(result2.c_str(), "e-"), "");

    static constexpr auto result3 = ce::atof("0.25e  -");
    static_assert(result3 == 0.25, "");
    // strtod の仕様だと OK
//  static_assert(ce::equal(result4.c_str(), "e  -"), "");

    return 0;
}

他人(=明日の自分)が読める気がしない…!

[コンパイラ]

  • g++ (GCC) 4.7.0 20110924 (experimental)