5

Proposal n4121 looks like it's going to add a std::string_literal type. It contains code like:

template<size_t n> struct string_literal { char data [n]; }

and:

template <size_t N> 
constexpr int stoi( const string_literal<N>& str, 
                    size_t* idx = 0, int base = 10); 

And here's my Number class:

template <typename T>
struct Number
{
private:
    T t;
public:
    constexpr Number(const T& t)
        : t(t)
    {
    }

    constexpr Number(const std::string& s);

    constexpr operator T() const
    {
        return t;
    }
};

template <>
constexpr Number<int>::Number(const std::string& s)
    : t(std::stoi(s))
{
}

I've taken a look at How do I convert a C string to a int at compile time? but it only works for C strings. and c_str() is non-constexpr. Also on top of that, this doesn't cover stol, stoul, stoll, stof, stod... and so on. Who knows if this proposal will make it into the standard. Also I don't want to wait 3 years for this library change to happen. How do I implement it in the moment, now?


Here's my attempt so far:

namespace lib
{
    constexpr bool is_digit(char c) {
        return c <= '9' && c >= '0';
    }

    constexpr int stoi_impl(const char* str, int value = 0) {
        return *str ?
                is_digit(*str) ?
                    stoi_impl(str + 1, (*str - '0') + value * 10)
                    : throw "compile-time-error: not a digit"
                : value;
    }

    constexpr int stoi(const char* str) {
        return stoi_impl(str);
    }

    template<size_t n> struct string_literal { char data [n]; };

    template < class charT, size_t N> 
    constexpr string_literal<N> 
        make_string_literal( const charT(&arr)[N])
        {
            string_literal<N> sl;
            for (std::size_t i = 0; i < N; ++i)
                sl.data[i] = arr[i];
            return sl;
        }
}

template <typename T>
struct Number
{
private:
    T t;
public:
    constexpr Number(const T& t)
        : t(t)
    {
    }

    constexpr Number(const std::size_t N, const lib::string_literal<N>& s);

    constexpr operator T() const
    {
        return t;
    }
};

template <>
constexpr Number<int>::Number(const std::size_t N, const lib::string_literal<N>& s)
    : t(lib::stoi(s.data))
{
}

int main()
{
    constexpr auto s = lib::make_string_literal("123456789");
    constexpr Number<int> n { sizeof(s.data), s };

    return 0;
}

main.cpp:44:69: error: non-type template argument is not a constant expression
    constexpr Number(const std::size_t N, const lib::string_literal<N>& s);
                                                                    ^
main.cpp:53:78: error: non-type template argument is not a constant expression
constexpr Number<int>::Number(const std::size_t N, const lib::string_literal<N>& s)
                                                                             ^
main.cpp:29:38: error: cannot initialize an array element of type 'char' with an lvalue of type 'const char [10]'
            return string_literal<N>{arr};
                                     ^~~
main.cpp:60:19: note: in instantiation of function template specialization 'lib::make_string_literal<char, 10>' requested here
    auto s = lib::make_string_literal("123456789");
6
  • 1
    You cannot use std::strings directly. However, try the str_const solution mentioned and linked to here. Commented Oct 26, 2014 at 1:32
  • So the problem is not choosing a function based on type, but converting from string to int at compile time. I think your title should reflect that. Commented Oct 26, 2014 at 1:33
  • How did you form your std::string at compile time if it wasn't using a string literal? Commented Oct 26, 2014 at 1:38
  • Why pass N when you can deduce it? template<size_t N> constexpr Number(const lib::string_literal<N>& s);? Commented Oct 26, 2014 at 1:59
  • 1
    Here's something I've written a while ago in C++14 style constexpr: stackoverflow.com/a/23445223 Commented Oct 26, 2014 at 12:05

1 Answer 1

1

I've implemented sophisticated compile-time string to scalar converters. Take a look at Constainer::strToFloat and Constainer::strToInt.

Examples from the testcase file:

static_assert( strToInt<int>(" 6849.") == 6849 );
static_assert( strToInt<signed char>(" -128aefws") == -128 );
static_assert( strToInt<unsigned>(" \t-0") == 0 );
static_assert( strToInt<unsigned>(" -0x0Xx", 0, 0) == 0 );
static_assert( strToInt<unsigned>(" +0xFF", 0, 0) == 0xFF );
static_assert( strToInt<unsigned>(" +077", 0, 0) == 7+8*7 );
static_assert( strToInt<unsigned>("11000", 0, 2) == 24 );

/**< These should go well on most implementations. */
static_assert( strToFloat<double>("+123.456789e0") == 123.456789 );
static_assert( strToFloat<double>("-0x1.Bc70a3D70A3d7p+6") == -111.11 );
static_assert( strToFloat<double     >("-1.18973e+4932") == -std::numeric_limits<double>::infinity() );
static_assert( strToFloat<long double>("-1.18973e+4932") != -std::numeric_limits<long double>::infinity() );
static_assert( strToFloat<double>("-0x.8p-1") == -0.25 );
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.