Added const_string class (constexpr string manipulations)
This commit is contained in:
parent
67f0f39b50
commit
3573a6fa74
76
constexpr/const_string.h
Normal file
76
constexpr/const_string.h
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef MESSMER_CPPUTILS_CONSTEXPR_CONST_STRING_H
|
||||
#define MESSMER_CPPUTILS_CONSTEXPR_CONST_STRING_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "impl/digit_parser.h"
|
||||
|
||||
namespace cpputils {
|
||||
class const_string {
|
||||
public:
|
||||
constexpr const_string(const char *str) : const_string(str, strlen(str)) { }
|
||||
|
||||
constexpr unsigned int size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
constexpr char operator[](unsigned int index) const {
|
||||
return (index < size()) ? _str[index] : throw std::logic_error("Index out of bounds");
|
||||
}
|
||||
|
||||
constexpr const_string dropPrefix(unsigned int prefixSize) const {
|
||||
return substr(prefixSize, _size - prefixSize);
|
||||
}
|
||||
|
||||
constexpr const_string dropSuffix(unsigned int suffixSize) const {
|
||||
return substr(0, _size - suffixSize);
|
||||
}
|
||||
|
||||
constexpr const_string substr(unsigned int start, unsigned int count) const {
|
||||
return (start + count <= size()) ? const_string(_str + start, count)
|
||||
: throw std::logic_error("Substring out of bounds");
|
||||
}
|
||||
|
||||
constexpr unsigned int sizeOfUIntPrefix() const {
|
||||
return _hasUIntPrefix() ? (1 + dropPrefix(1).sizeOfUIntPrefix()) : 0;
|
||||
}
|
||||
|
||||
constexpr unsigned int parseUIntPrefix() const {
|
||||
return _hasUIntPrefix() ? _parseUIntBackwards(_str + sizeOfUIntPrefix() - 1, sizeOfUIntPrefix())
|
||||
: throw std::logic_error("Not a valid number");
|
||||
}
|
||||
|
||||
constexpr const_string dropUIntPrefix() const {
|
||||
return _hasUIntPrefix() ? dropPrefix(1).dropUIntPrefix() : *this;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const const_string &rhs) const {
|
||||
return _size == rhs._size && 0 == memcmp(_str, rhs._str, _size);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const const_string &rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::string toStdString() {
|
||||
return std::string(_str, _size);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr const_string(const char *str, unsigned int size) : _str(str), _size(size) { }
|
||||
|
||||
static constexpr unsigned int _parseUIntBackwards(const char *input, unsigned int numDigits) {
|
||||
return (numDigits == 0) ? 0 : (digit_parser::parseDigit(*input) +
|
||||
10 * _parseUIntBackwards(input - 1, numDigits - 1));
|
||||
}
|
||||
|
||||
constexpr bool _hasUIntPrefix() const {
|
||||
return size() > 0 && digit_parser::isDigit(_str[0]);
|
||||
}
|
||||
|
||||
const char *_str;
|
||||
unsigned int _size;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
30
constexpr/impl/digit_parser.h
Normal file
30
constexpr/impl/digit_parser.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef MESSMER_CPPUTILS_CONSTEXPR_DIGIT_PARSER_H
|
||||
#define MESSMER_CPPUTILS_CONSTEXPR_DIGIT_PARSER_H
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cpputils {
|
||||
class digit_parser {
|
||||
public:
|
||||
static constexpr bool isDigit(char digit) {
|
||||
return digit == '0' || digit == '1' || digit == '2' || digit == '3' || digit == '4' || digit == '5' ||
|
||||
digit == '6' || digit == '7' || digit == '8' || digit == '9';
|
||||
}
|
||||
|
||||
static constexpr unsigned char parseDigit(char digit) {
|
||||
return (digit == '0') ? 0 :
|
||||
(digit == '1') ? 1 :
|
||||
(digit == '2') ? 2 :
|
||||
(digit == '3') ? 3 :
|
||||
(digit == '4') ? 4 :
|
||||
(digit == '5') ? 5 :
|
||||
(digit == '6') ? 6 :
|
||||
(digit == '7') ? 7 :
|
||||
(digit == '8') ? 8 :
|
||||
(digit == '9') ? 9 :
|
||||
throw std::logic_error("Not a valid digit");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
125
test/constexpr/const_string_test.cpp
Normal file
125
test/constexpr/const_string_test.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#include "../../constexpr/const_string.h"
|
||||
#include <google/gtest/gtest.h>
|
||||
|
||||
using namespace cpputils;
|
||||
|
||||
// ----------------------------------------------
|
||||
// size()
|
||||
// ----------------------------------------------
|
||||
static_assert(6 == const_string("Hello ").size(), "Size of \"Hello \" is 6");
|
||||
static_assert(1 == const_string(" ").size(), "Size of \" \" is 6");
|
||||
static_assert(0 == const_string("").size(), "Size of \"\" is 6");
|
||||
|
||||
|
||||
// ----------------------------------------------
|
||||
// operator[]
|
||||
// ----------------------------------------------
|
||||
static_assert('a' == const_string("a")[0], "\"a\"[0] == 'a'");
|
||||
static_assert('a' == const_string("abc")[0], "\"abc\"[0] == 'a'");
|
||||
static_assert('b' == const_string("abc")[1], "\"abc\"[1] == 'b'");
|
||||
static_assert('c' == const_string("abc")[2], "\"abc\"[1] == 'c'");
|
||||
|
||||
static_assert('c' == const_string("abc").dropPrefix(1)[1], "operator[] is not broken after calling dropPrefix()");
|
||||
|
||||
// ----------------------------------------------
|
||||
// operator== and operator!=
|
||||
// ----------------------------------------------
|
||||
static_assert(const_string("") == const_string(""), "\"\" should be equal to \"\"");
|
||||
static_assert(const_string("a") == const_string("a"), "\"a\" should be equal to \"a\"");
|
||||
static_assert(const_string("ab") == const_string("ab"), "\"ab\" should be equal to \"ab\"");
|
||||
static_assert(!(const_string("") != const_string("")), "\"\" should not be different to \"\"");
|
||||
static_assert(!(const_string("a") != const_string("a")), "\"a\" should not be different to \"a\"");
|
||||
static_assert(!(const_string("ab") != const_string("ab")), "\"ab\" should not be different to \"ab\"");
|
||||
|
||||
static_assert(!(const_string("a") == const_string("A")), "\"a\" should not be equal to \"A\"");
|
||||
static_assert(!(const_string("ab") == const_string("abc")), "\"ab\" should not be equal to \"abc\"");
|
||||
static_assert(!(const_string("abc") == const_string("ab")), "\"abc\" should not be equal to \"ab\"");
|
||||
static_assert(!(const_string("a") == const_string("")), "\"a\" should not be equal to \"\"");
|
||||
static_assert(!(const_string("") == const_string("a")), "\"\" should not be equal to \"a\"");
|
||||
static_assert(const_string("a") != const_string("A"), "\"a\" should be different to \"A\"");
|
||||
static_assert(const_string("ab") != const_string("abc"), "\"ab\" should be different to \"abc\"");
|
||||
static_assert(const_string("abc") != const_string("ab"), "\"abc\" should be different to \"ab\"");
|
||||
static_assert(const_string("a") != const_string(""), "\"a\" should be different to \"\"");
|
||||
static_assert(const_string("") != const_string("a"), "\"\" should be different to \"a\"");
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------
|
||||
// dropPrefix(), dropSuffix() and substr()
|
||||
// ----------------------------------------------
|
||||
static_assert(const_string("bc") == const_string("abc").dropPrefix(1),
|
||||
"Dropping the first character of \"abc\" should yield \"bc\"");
|
||||
static_assert(const_string("c") == const_string("abc").dropPrefix(1).dropPrefix(1),
|
||||
"Dropping prefixes should be chainable");
|
||||
static_assert(const_string("c") == const_string("abc").dropPrefix(2),
|
||||
"Dropping the first two characters of \"abc\" should yield \"c\"");
|
||||
static_assert(const_string("") == const_string("abc").dropPrefix(3),
|
||||
"Dropping the first three characters of \"abc\" should yield \"\"");
|
||||
|
||||
static_assert(const_string("ab") == const_string("abc").dropSuffix(1),
|
||||
"Dropping the last character of \"abc\" should yield \"ab\"");
|
||||
static_assert(const_string("a") == const_string("abc").dropSuffix(1).dropSuffix(1),
|
||||
"Dropping suffixes should be chainable");
|
||||
static_assert(const_string("a") == const_string("abc").dropSuffix(2),
|
||||
"Dropping the last two characters of \"abc\" should yield \"a\"");
|
||||
static_assert(const_string("") == const_string("abc").dropSuffix(3),
|
||||
"Dropping the last three characters of \"abc\" should yield \"\"");
|
||||
|
||||
static_assert(const_string("bc") == const_string("abc").substr(1, 2),
|
||||
"Dropping the first character of \"abc\" should yield \"bc\"");
|
||||
static_assert(const_string("ab") == const_string("abc").substr(0, 2),
|
||||
"Dropping the last character of \"abc\" should yield \"ab\"");
|
||||
static_assert(const_string("bc") == const_string("abcd").substr(1, 2),
|
||||
"Dropping the first and last character of \"abcd\" should yield \"bc\"");
|
||||
|
||||
constexpr const_string val = const_string("abc");
|
||||
static_assert(val.dropSuffix(1) != val,
|
||||
"Even when working with the same underlying object, dropping a suffix makes it a different object.");
|
||||
static_assert(val.dropPrefix(1) != val,
|
||||
"Even when working with the same underlying object, dropping a prefix makes it a different object.");
|
||||
|
||||
|
||||
// ----------------------------------------------
|
||||
// sizeOfUIntPrefix(), parseUIntPrefix() and dropUIntPrefix()
|
||||
// ----------------------------------------------
|
||||
|
||||
static_assert(0 == const_string("ab").sizeOfUIntPrefix(), "\"ab\" doesn't have any digits");
|
||||
static_assert(0 == const_string("").sizeOfUIntPrefix(), "\"\" doesn't have any digits");
|
||||
static_assert(1 == const_string("5").sizeOfUIntPrefix(), "\"5\" has 1 digit");
|
||||
static_assert(1 == const_string("5a").sizeOfUIntPrefix(), "\"5\" has 1 digit");
|
||||
static_assert(10 == const_string("5594839203a").sizeOfUIntPrefix(), "\"5594839203a\" has 1 digit");
|
||||
static_assert(10 == const_string("5594839203").sizeOfUIntPrefix(), "\"5594839203\" has 1 digit");
|
||||
|
||||
static_assert(0 == const_string("0").parseUIntPrefix(), "\"0\" should be correctly parsed");
|
||||
static_assert(0 == const_string("0a").parseUIntPrefix(), "\"0a\" should be correctly parsed");
|
||||
static_assert(0 == const_string("0.").parseUIntPrefix(), "\"0.\" should be correctly parsed");
|
||||
static_assert(3 == const_string("3").parseUIntPrefix(), "\"3\" should be correctly parsed");
|
||||
static_assert(12 == const_string("12").parseUIntPrefix(), "\"12\" should be correctly parsed");
|
||||
static_assert(123 == const_string("123").parseUIntPrefix(), "\"123\" should be correctly parsed");
|
||||
static_assert(123 == const_string("0123").parseUIntPrefix(), "\"0123\" should be correctly parsed");
|
||||
static_assert(1 == const_string("001a").parseUIntPrefix(), "\"001a\" should be correctly parsed");
|
||||
static_assert(1230 == const_string("1230").parseUIntPrefix(), "\"1230\" should be correctly parsed");
|
||||
static_assert(1230 == const_string("1230beta").parseUIntPrefix(), "\"1230beta\" should be correctly parsed");
|
||||
static_assert(357532 == const_string("357532").parseUIntPrefix(), "\"357532\" should be correctly parsed");
|
||||
static_assert(357532 == const_string("357532alpha").parseUIntPrefix(), "\"357532alpha\" should be correctly parsed");
|
||||
static_assert(357532 == const_string("357532.4").parseUIntPrefix(), "\"357532.4\" should be correctly parsed");
|
||||
|
||||
static_assert(const_string("bla") == const_string("bla").dropUIntPrefix(), "\"bla\" has no number to skip");
|
||||
static_assert(const_string("alpha") == const_string("0alpha").dropUIntPrefix(), "\"0alpha\" should skip the 0");
|
||||
static_assert(const_string(".3alpha") == const_string("12.3alpha").dropUIntPrefix(),
|
||||
"\"12.3alpha\" should skip the 12");
|
||||
static_assert(const_string("-5") == const_string("-5").dropUIntPrefix(),
|
||||
"\"-5\" is not unsigned and should skip nothing");
|
||||
static_assert(const_string("") == const_string("").dropUIntPrefix(), "\"\" should skip nothing");
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------
|
||||
// toStdString()
|
||||
// ----------------------------------------------
|
||||
TEST(const_string_test, toStdString) {
|
||||
EXPECT_EQ("", const_string("").toStdString());
|
||||
EXPECT_EQ("a", const_string("a").toStdString());
|
||||
EXPECT_EQ("abc", const_string("abc").toStdString());
|
||||
EXPECT_EQ("abc", const_string("prefix_abc_suffix").substr(7,3).toStdString());
|
||||
}
|
30
test/constexpr/impl/digit_parser_test.cpp
Normal file
30
test/constexpr/impl/digit_parser_test.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "../../../constexpr/impl/digit_parser.h"
|
||||
|
||||
using namespace cpputils;
|
||||
|
||||
static_assert(digit_parser::isDigit('0'), "'0' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('1'), "'1' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('2'), "'2' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('3'), "'3' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('4'), "'4' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('5'), "'5' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('6'), "'6' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('7'), "'7' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('8'), "'8' should be recognized as a digit");
|
||||
static_assert(digit_parser::isDigit('9'), "'9' should be recognized as a digit");
|
||||
static_assert(!digit_parser::isDigit('a'), "'a' should not be recognized as a digit");
|
||||
static_assert(!digit_parser::isDigit('.'), "'.' should not be recognized as a digit");
|
||||
static_assert(!digit_parser::isDigit('-'), "'-' should not be recognized as a digit");
|
||||
static_assert(!digit_parser::isDigit('/'), "'/' should not be recognized as a digit");
|
||||
static_assert(!digit_parser::isDigit(' '), "' ' should not be recognized as a digit");
|
||||
|
||||
static_assert(0 == digit_parser::parseDigit('0'), "'0' should be correctly parsed");
|
||||
static_assert(1 == digit_parser::parseDigit('1'), "'1' should be correctly parsed");
|
||||
static_assert(2 == digit_parser::parseDigit('2'), "'2' should be correctly parsed");
|
||||
static_assert(3 == digit_parser::parseDigit('3'), "'3' should be correctly parsed");
|
||||
static_assert(4 == digit_parser::parseDigit('4'), "'4' should be correctly parsed");
|
||||
static_assert(5 == digit_parser::parseDigit('5'), "'5' should be correctly parsed");
|
||||
static_assert(6 == digit_parser::parseDigit('6'), "'6' should be correctly parsed");
|
||||
static_assert(7 == digit_parser::parseDigit('7'), "'7' should be correctly parsed");
|
||||
static_assert(8 == digit_parser::parseDigit('8'), "'8' should be correctly parsed");
|
||||
static_assert(9 == digit_parser::parseDigit('9'), "'9' should be correctly parsed");
|
Loading…
x
Reference in New Issue
Block a user