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");
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 );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With