openspades/Sources/Core/Strings.h

295 lines
9.5 KiB
C++

/*
Copyright (c) 2013 yvt
This file is part of OpenSpades.
OpenSpades is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include "Math.h"
#include <array>
#include <cstring>
#include "Exception.h"
#include <cstdlib>
namespace spades {
std::string Intern(const std::string&);
template<typename T>
std::string ToString(const T& v) {
return std::to_string(v);
}
template<typename F>
std::size_t StringSpan(const char *str, F predicate) {
auto *ptr = str;
while(*ptr != 0 && predicate(*ptr)) ptr++;
return ptr - str;
}
class StandardTokenizer {
const char *ptr;
public:
StandardTokenizer(const char *ptr);
class Iterator {
const char *ptr;
std::string token;
std::string prevToken;
bool nowPrev;
void SkipWhitespace();
void AnalyzeToken();
public:
Iterator(const char *ptr);
std::string operator *();
void operator ++();
void operator --();
bool operator ==(const Iterator& it) const {
return ptr == it.ptr && nowPrev == it.nowPrev;
}
bool operator !=(const Iterator& it) const {
return ptr != it.ptr || nowPrev != it.nowPrev;
}
};
Iterator begin();
Iterator end();
};
template<typename T, int N = 32>
class StaticArray {
std::array<T, N> staticArray;
std::vector<T> dynamicArray;
std::size_t count;
public:
StaticArray(): count(0) {}
std::size_t size() const { return count; }
void push_back(const T& e) {
if(count == N) { dynamicArray.push_back(e); count++;
}else{ staticArray[count++] = e; }
}
void push_back(T&& e) {
if(count == N) { dynamicArray.push_back(e); count++;
}else{ staticArray[count++] = e; }
}
T& operator [](std::size_t i){
if(i < N) return staticArray[i];
else return dynamicArray[i - N];
}
const T& operator [](std::size_t i) const{
if(i < N) return staticArray[i];
else return dynamicArray[i - N];
}
class iterator {
StaticArray<T,N>& arr;
mutable std::size_t index;
iterator(StaticArray<T,N>& arr, std::size_t index):
arr(arr), index(index) {}
T& operator *() { return arr[index]; }
const T& operator *() const { return arr[index]; }
iterator operator +(std::size_t i) { return iterator(arr, index + i); }
iterator operator -(std::size_t i) { return iterator(arr, index - i); }
const iterator operator +(std::size_t i) const { return iterator(arr, index + i); }
const iterator operator -(std::size_t i) const { return iterator(arr, index - i); }
void operator ++() const { ++index; }
void operator --() const { --index; }
T& operator[](std::size_t i) { return arr[index + i]; }
const T& operator[](std::size_t i) const { return arr[index + i]; }
bool operator ==(const iterator& it) const {return &arr == &it.arr && index == it.index; }
bool operator !=(const iterator& it) const {return &arr != &it.arr || index != it.index; }
};
typedef const iterator const_iterator;
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, size()); }
const_iterator begin() const { return iterator(*this, 0); }
const_iterator end() const { return iterator(*this, size()); }
};
template<std::size_t numArgs>
class Formatter {
struct Segment {
std::size_t start;
std::size_t length;
};
struct Placeholder {
std::size_t parameterIndex;
std::string format;
};
const char *str;
StaticArray<Segment> segments;
StaticArray<Placeholder> placeholders;
std::array<std::string, numArgs> parameters;
public:
Formatter(const char *str):str(str){
auto *firstPlaceholder = std::strchr(str, '{');
if(firstPlaceholder) {
std::size_t index = firstPlaceholder - str;
segments.push_back({0, index});
while(str[index] != '\0') {
// parse placeholder.
index++;
auto endIndex = index + std::strcspn(str + index, ":}");
if(str[endIndex] == '\0')
SPRaise("Malformed format string (placeholder is not closed): %s", str);
char *endptr;
auto idx = static_cast<std::size_t>(strtol(str + index, &endptr, 10));
if(endptr != str + endIndex) {
SPRaise("Malformed format string (failed to parse parameter index): %s", str);
}
Placeholder p;
p.parameterIndex = idx;
if(idx >= numArgs) {
SPRaise("Malformed format string (using non-existent parameter index %d): %s", static_cast<int>(idx), str);
}
if(str[endIndex] == ':') {
index = endIndex + 1;
endptr = const_cast<char*>(std::strchr(str + index, '}'));
if(!endptr)
SPRaise("Malformed format string (placeholder is not closed): %s", str);
endIndex = endptr - str;
p.format = std::string(str + index, endIndex - index);
index = endIndex + 1;
}else{
index = endIndex + 1;
}
placeholders.push_back(p);
// find next placeholder
auto *nextPlaceholder = std::strchr(str + index, '{');
if(nextPlaceholder) {
endIndex = nextPlaceholder - str;
}else{
endIndex = std::strlen(str);
}
segments.push_back({index, endIndex - index});
index = endIndex;
}
}else{
segments.push_back({0, std::strlen(str)});
}
}
private:
template<std::size_t index = 0, class ...T>
void SetParameterValue(T... args) {
static_assert(index <= numArgs, "index <= numArgs");
}
template<std::size_t index = 0, class Head, class ...T>
void SetParameterValue(Head head, T... args) {
static_assert(index < numArgs, "index < numArgs");
parameters[index] = ToString(head);
SetParameterValue<index + 1, T...>(args...);
}
public:
template<class ...T>
std::string Format(T... args) {
SetParameterValue<>(args...);
std::string ret;
ret.append(str + segments[0].start, segments[0].length);
for(std::size_t i = 0; i < placeholders.size(); i++) {
ret += parameters[placeholders[i].parameterIndex];
ret.append(str + segments[i + 1].start, segments[i + 1].length);
}
return ret;
}
};
template<>
class Formatter<0> {
const char *fmt;
public:
Formatter(const char *fmt):fmt(fmt) {
if(strchr(fmt, '{')) {
SPRaise("Malformed format string (using non-existent parameter. no parameter provided): %s", fmt);
}
}
std::string Format() {
return fmt;
}
};
template<class ...T>
std::string Format(const char *str, T... args) {
return Formatter<sizeof...(args)>(str).Format(args...);
}
template<class ...T>
std::string Format(const std::string& str, T... args) {
return Format(str.c_str(), args...);
}
template<> std::string ToString<std::string>(const std::string& s);
template<> std::string ToString<const char *>(const char *const&s);
template<> std::string ToString<Vector2>(const Vector2& v);
template<> std::string ToString<Vector3>(const Vector3& v);
template<> std::string ToString<Vector4>(const Vector4& v);
template<> std::string ToString<IntVector3>(const IntVector3& v);
template<class ...T> int CheckPlural(T... args) { return 1; }
template<class ...T> int CheckPlural(int v, T... args) { return v; }
template<class ...T> int CheckPlural(long v, T... args) { return v; }
template<class ...T> int CheckPlural(unsigned int v, T... args) { return v; }
template<class ...T> int CheckPlural(unsigned long v, T... args) { return v; }
template<class ...T> int CheckPlural(short v, T... args) { return v; }
template<class ...T> int CheckPlural(unsigned short v, T... args) { return v; }
template<class ...T> int CheckPlural(char v, T... args) { return v; }
template<class ...T> int CheckPlural(unsigned char v, T... args) { return v; }
std::string GetTextRaw(const std::string& domain, const std::string& ctx, const std::string& text, int plural);
std::string GetTextRawPlural(const std::string& domain, const std::string& ctx, const std::string& text, const std::string& textPlural, int plural);
template<class ...T>
std::string GetText(const std::string& domain, const std::string& context, const std::string& text, T... args) {
int plural = CheckPlural(args...);
auto s = GetTextRaw(domain, context, text, plural);
return Format(s, args...);
}
template<class ...T>
std::string GetTextPlural(const std::string& domain, const std::string& context, const std::string& text, const std::string& textPl, T... args) {
int plural = CheckPlural(args...);
auto s = GetTextRawPlural(domain, context, text, textPl, plural);
return Format(s, args...);
}
class CatalogDomainHandle {
std::string domain;
public:
CatalogDomainHandle(const std::string& domainName);
template<class ...T>
std::string Get(const std::string& context, const std::string& text, T... args) {
return GetText(domain, context, text, args...);
}
template<class ...T>
std::string GetPlural(const std::string& context, const std::string& text, const std::string& textPl, T... args) {
return GetTextPlural(domain, context, text, textPl, args...);
}
};
extern CatalogDomainHandle defaultDomain;
void LoadCurrentLocale();
}
#define _Tr(...) ::spades::defaultDomain.Get(__VA_ARGS__)
#define _TrN(...) ::spades::defaultDomain.GetPlural(__VA_ARGS__)
#define SPADES_DEFINE_GETTEXT_DOMAIN(name) \
static CatalogDomainHandle _ ## name (# name)