【发布时间】:2020-04-19 11:04:19
【问题描述】:
我想在编译时实现 atoi() 函数(在 C++ 语言中,使用 C++11 或 C++14 标准)。所以它应该能够将双引号括起来的文本解析为数字,或者报告错误。更具体地说,它是更大系统的一部分,能够在编译时解析类似 printf 的格式。而且我想在单词上拆分格式字符串,如果某些特定单词可以用数字表示 - 输出数字而不是字符串(场景后面是序列化程序类,它可以比字符串更有效地序列化数字,而且更重要的是,反序列化器不应该尝试将每个字符串解析为数字,因为在格式字符串中打印的所有数字始终表示为数字,而不是字符串)...
据我所知,有两种方法可以解决该任务:
1) 使用 constexpr 函数;
2) 通过模板元编程。
哪种方式更好?我已经尝试过第一种方式,我可以看到这种方式有很多障碍:特别是与 c++11 相关的限制很少。看起来第二个可能更可取,但它需要一些技巧(您需要使用 of operator"" 将 c-string 拆分为分隔字符,从 c++14 开始的 gcc 和从 c++11 开始的 clangs 支持)。此外,完全基于 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”)用双引号写成整数类型的值。我的解决方案看起来太麻烦了。
【问题讨论】:
-
其实我一直在寻找这样的东西。它可能在编译时用于散列字符串或某事。另一种方法可能是编译器 api 编程,但这将是特定于编译器的。
-
C++17 不是一个选项,对吧?
-
那么,您的解决方案有什么问题?你到底想知道什么?在什么意义上更好?
-
@MarcinPoloczek 正如你提到的编译时字符串散列:也许你喜欢我前段时间在 SO 上写的这篇文章:stackoverflow.com/a/47081012/8494588
标签: c++ c++11 template-meta-programming compile-time compile-time-constant