Ich möchte die Funktion atoi () zur Kompilierungszeit implementieren (in C ++ - Sprache unter Verwendung des C ++ 11- oder C ++ 14-Standards). Es sollte also in der Lage sein, in doppelten Anführungszeichen eingeschlossenen Text als Zahl zu analysieren oder einen Fehler zu melden. Insbesondere ist es Teil eines größeren Systems, das das druckähnliche Format zur Kompilierungszeit analysieren kann. Und ich möchte Formatzeichenfolgen auf Wörter aufteilen und wenn ein bestimmtes Wort durch eine Zahl dargestellt werden kann - geben Sie die Nummer anstelle der Zeichenfolge aus (hinter den Kulissen befindet sich die Serializer-Klasse, die Zahlen effektiver serialisieren kann als die Zeichenfolgen und die mehr ist Wichtig ist, dass der Deserializer dann nicht versuchen sollte, jede Zeichenfolge als Zahl zu analysieren, da alle Zahlen, die innerhalb der Formatzeichenfolge gedruckt werden, immer als Zahlen dargestellt werden, jedoch nicht als Zeichenfolgen.
Wie ich zwei kenne, kann es zwei Ansätze geben, um die Aufgabe zu lösen:
1) unter Verwendung von constexpr-Funktionen;
2) durch Template-Metaprogrammierung.
Welcher Weg kann besser sein? Ich habe den ersten Weg ausprobiert und sehe, dass es auf diese Weise viele Hindernisse gibt: insbesondere wenige Einschränkungen in Bezug auf c ++ 11. Es sieht so aus, als wäre eine zweite vorzuziehen, erfordert jedoch einige Tricks (Sie müssen den C-String teilen, um die Zeichen mithilfe des Operators "" zu trennen, der in gcc ab C ++ 14 und in Clangs ab C ++ 11 unterstützt wird ). Auch eine vollständig auf TMP basierende Lösung ist möglicherweise zu groß und zu verworren.
Unten ist meine Lösung, ich freue mich über einige Vorschläge dazu.
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;
}
Grundsätzlich möchte ich fragen, wie ich zur Kompilierungszeit eine Zahl (wie "42") in doppelte Anführungszeichen in den Wert des Integraltyps umwandeln kann . Meine Lösung sieht zu umständlich aus.
Antworten:
Mit C ++ 14 können wir das Makro und einige ternäre Operatoren entfernen. Hier ist, wie ich es machen würde, der Code sollte selbsterklärend sein (ich habe auch einige Kommentare hinzugefügt). Der folgende Code kann auch hier (mit einigen Beispielen) zum Compiler-Vergleich gefunden werden.
C ++ 17 könnte die Codemenge durch Verwendung noch weiter reduzieren
std::string_view
. IhrResult<T>
könnte auch durch ersetzt werdenstd::optional
.quelle