Quiero implementar la función atoi () en tiempo de compilación (en lenguaje C ++, usando el estándar C ++ 11 o C ++ 14). Por lo tanto, debería poder analizar el texto encerrado entre comillas dobles como número o informar un error. Más específicamente, es parte de un sistema más grande, que es capaz de analizar el formato tipo printf en tiempo de compilación. Y quiero dividir cadenas de formato en palabras y, si alguna palabra en particular se puede representar por número, número de salida en lugar de la cadena (detrás de la escena hay una clase de serializador, que puede serializar números de manera más efectiva que las cadenas, y que es más importante, el deserializador no debería intentar analizar cada cadena como un número, porque todos los numeradores impresos dentro de la cadena de formato siempre se representan como números, pero no como cadenas) ...
Como sé dos, puede haber dos enfoques para resolver la tarea:
1) mediante el uso de funciones constexpr;
2) por metaprogramación de plantilla.
¿Qué camino puede ser mejor? Lo he intentado por primera vez, y puedo ver que hay muchos obstáculos de esta manera: especialmente pocas limitaciones relacionadas con c ++ 11. Parece que el segundo puede ser preferible, pero requiere algunos trucos (es necesario dividir la cadena en C para separar los caracteres mediante el operador "", que es compatible con gcc a partir de c ++ 14 y en clangs a partir de c ++ 11 ) Además, la solución basada completamente en TMP puede ser demasiado grande y demasiado enredada.
A continuación se muestra mi solución, me alegra escuchar algunas sugerencias al respecto.
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;
}
Básicamente quiero preguntar, ¿cómo puedo transformar en el número de tiempo de compilación (como "42") escrito entre comillas dobles en el valor del tipo integral. Mi solución parece demasiado engorrosa.
Respuestas:
Con C ++ 14 podemos deshacernos de la macro y algunos operadores ternarios. Así es como lo haría, el código debería ser autoexplicativo (también he agregado algunos comentarios). El código a continuación también se puede encontrar aquí (con algunos ejemplos) para la comparación del compilador.
C ++ 17 podría reducir la cantidad de código aún más mediante el uso
std::string_view
. TuResult<T>
también podría ser reemplazado porstd::optional
.fuente