コンパイル時にatoi()関数を実装したい(C ++ 11またはC ++ 14標準を使用して、C ++言語で)。したがって、二重引用符で囲まれたテキストを数値として解析したり、エラーを再現したりできます。より具体的には、それはコンパイル時にprintfのような形式を解析できるより大きなシステムの一部です。そして、私は単語のフォーマット文字列を分割し、特定の単語を数字で表すことができる場合は、文字列ではなく番号を出力します(シーンの背後にあるシリアライザクラスは、文字列よりも数値をより効率的にシリアル化できます。重要なのは、デシリアライザはすべての文字列を数値として解析しようとしないことです。これは、フォーマット文字列内に出力されるすべての数値は常に数値として表現され、文字列としては表現されないためです...)
私が2つ知っているように、タスクを解決するには2つの方法があります。
1)constexpr関数を使用する。
2)テンプレートのメタプログラミング。
どっちがいいの?私は最初の方法を試しましたが、この方法には多くの障害があることがわかります。特に、c ++ 11に関連するいくつかの制限があります。2番目のように見えるかもしれませんが、いくつかのトリックが必要です(c ++ 14から始まるgccおよびc ++ 11から始まるclangでサポートされている、演算子 ""を使用して文字列を分割して文字を分離する必要があります。 )。また、完全にTMPに基づくソリューションは、大きすぎて複雑すぎます。
以下は私の解決策です、私はそれについていくつかの提案を聞いてうれしいです。
http://coliru.stacked-crooked.com/a/0b8f1fae9d9b714b
#include <stdio.h>
template <typename T> struct Result
{
T value;
bool valid;
constexpr Result(T v) : value(v), valid(true) {}
constexpr Result() : value(), valid(false) {}
};
template <typename T>
constexpr Result<T> _atoi_oct(const char *s, size_t n, T val, int sign)
{
return n == 0 ? Result<T>(sign < 0 ? -val : val)
: *s >= '0' && *s <= '7'
? _atoi_oct(s+1, n-1, val*T(010) + *s - '0', sign)
: Result<T>();
}
template <typename T>
constexpr Result<T> _atoi_dec(const char *s, size_t n, T val, int sign)
{
return n == 0 ? Result<T>(sign < 0 ? -val : val)
: *s >= '0' && *s <= '9'
? _atoi_dec(s+1, n-1, val*T(10) + *s - '0', sign)
: Result<T>();
}
template <typename T>
constexpr Result<T> _atoi_hex(const char *s, size_t n, T val, int sign)
{
return n == 0 ? Result<T>(sign < 0 ? -val : val)
: *s >= '0' && *s <= '9'
? _atoi_hex(s+1, n-1, val*T(0x10) + *s - '0', sign)
: *s >= 'a' && *s <= 'f'
? _atoi_hex(s+1, n-1, val*T(0x10) + *s - 'a' + 10, sign)
: *s >= 'A' && *s <= 'F'
? _atoi_hex(s+1, n-1, val*T(0x10) + *s - 'A' + 10, sign)
: Result<T>();
}
template <typename T>
constexpr Result<T> _atoi_zero(const char *s, size_t n, int sign = 1)
{
return n == 0 ? Result<T>()
: *s >= '0' && *s <= '7'
? _atoi_oct(s+1, n-1, T(*s - '0'), sign)
: *s == 'x' || *s == 'X'
? _atoi_hex(s+1, n-1, T(0), sign)
: Result<T>();
}
template <typename T>
constexpr Result<T> _atoi_sign(const char *s, size_t n, int sign = 1)
{
return n == 0 ? Result<T>()
: *s == '0'
? _atoi_zero<T>(s+1, n-1, sign)
: *s > '0' && *s <= '9'
? _atoi_dec(s+1, n-1, T(*s - '0'), sign)
: Result<T>();
}
template <typename T>
constexpr Result<T> _atoi_space(const char *s, size_t n)
{
return n == 0 ? Result<T>()
: (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r' || *s == '\v')
? _atoi_space<T>(s+1, n-1)
: *s == '-'
? _atoi_sign<T>(s+1, n-1, -1)
: *s == '+'
? _atoi_sign<T>(s+1, n-1)
: *s == '0'
? _atoi_zero<T>(s+1, n-1)
: _atoi_dec(s, n, T(0), 1);
}
template <size_t N> void pstr(const char (&s)[N])
{
printf("s '%.*s'\n", int(N-1), s);
}
template <typename Str>
__attribute__((always_inline))
void _atoi(Str s)
{
constexpr auto result = _atoi_space<long>(s.cstr(), sizeof(s.cstr())-1);
if (result.valid)
printf("i %ld\n", result.value);
else
pstr(reinterpret_cast<const char (&)[sizeof(s.cstr())]>(s.cstr()));
}
#define atoi(STR) _atoi([]() { \
struct S { \
static constexpr const char (&cstr())[sizeof(STR)] { return STR; } \
}; \
return S(); \
}())
int main()
{
atoi("42");
atoi("-1");
atoi("+1");
atoi("010");
atoi("-0x10");
atoi("--1");
atoi("x");
atoi("3x");
return 0;
}
基本的に、整数型の値に二重引用符で囲まれた数値( "42"など)をコンパイル時に変換するにはどうすればよいですか。私のソリューションは面倒すぎます。