|
|
@@ -6,147 +6,151 @@ import Core.Types;
|
|
|
|
|
|
export import Core.StringFormat;
|
|
|
|
|
|
-#define TO_STRING(type) \
|
|
|
- size_t toString(type t, char* s, size_t n, const StringFormat& format = {})
|
|
|
-
|
|
|
export namespace Core {
|
|
|
- TO_STRING(signed char);
|
|
|
- TO_STRING(char);
|
|
|
- TO_STRING(short);
|
|
|
- TO_STRING(int);
|
|
|
- TO_STRING(long);
|
|
|
- TO_STRING(long long);
|
|
|
- TO_STRING(unsigned char);
|
|
|
- TO_STRING(unsigned short);
|
|
|
- TO_STRING(unsigned int);
|
|
|
- TO_STRING(unsigned long);
|
|
|
- TO_STRING(unsigned long long);
|
|
|
- TO_STRING(float);
|
|
|
- TO_STRING(double);
|
|
|
- TO_STRING(const char*);
|
|
|
- TO_STRING(const unsigned char*);
|
|
|
- TO_STRING(unsigned char*);
|
|
|
- TO_STRING(bool);
|
|
|
-
|
|
|
- template<typename T>
|
|
|
- void addString(
|
|
|
- const T& t, char*& s, size_t& n, size_t& total,
|
|
|
- const StringFormat& format = {});
|
|
|
-
|
|
|
- template<Core::Iterable T>
|
|
|
- size_t toString(
|
|
|
- const T& t, char* s, size_t n, const StringFormat& format = {}) {
|
|
|
- (void)format;
|
|
|
- size_t total = 0;
|
|
|
- addString("[", s, n, total);
|
|
|
- auto current = t.begin();
|
|
|
- auto end = t.end();
|
|
|
- if(current != end) {
|
|
|
- addString(*current, s, n, total);
|
|
|
- ++current;
|
|
|
- }
|
|
|
- while(current != end) {
|
|
|
- addString(", ", s, n, total);
|
|
|
- addString(*current, s, n, total);
|
|
|
- ++current;
|
|
|
- }
|
|
|
- addString("]", s, n, total);
|
|
|
- return total;
|
|
|
- }
|
|
|
-
|
|
|
- template<typename T>
|
|
|
- concept ToString =
|
|
|
- requires(const T& t, char* s, size_t n) { t.toString(s, n); };
|
|
|
-
|
|
|
- template<ToString T>
|
|
|
- size_t toString(
|
|
|
- const T& t, char* s, size_t n, const StringFormat& format = {}) {
|
|
|
- (void)format;
|
|
|
- return t.toString(s, n);
|
|
|
- }
|
|
|
+ class StringBase;
|
|
|
|
|
|
template<typename T>
|
|
|
- void addString(
|
|
|
- const T& t, char*& s, size_t& n, size_t& total,
|
|
|
- const StringFormat& format) {
|
|
|
- size_t w = toString(t, s, n, format);
|
|
|
- total += w;
|
|
|
- w = Core::min(n, w);
|
|
|
- s += w;
|
|
|
- n -= w;
|
|
|
- }
|
|
|
+ concept ToString = requires(const T& t, StringBase& b) { t.toString(b); };
|
|
|
|
|
|
- size_t copyFormatUntil(
|
|
|
- const char*& format, char*& s, size_t& n, StringFormat& f);
|
|
|
-
|
|
|
- template<typename T, typename... Args>
|
|
|
- void formatR(
|
|
|
- const char*& format, char*& s, size_t& n, size_t& total, const T& t,
|
|
|
- Args&&... args) {
|
|
|
- StringFormat f;
|
|
|
- total += copyFormatUntil(format, s, n, f);
|
|
|
- addString(t, s, n, total, f);
|
|
|
- if constexpr(sizeof...(args) > 0) {
|
|
|
- formatR(format, s, n, total, Core::forward<Args>(args)...);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- template<typename... Args>
|
|
|
- size_t formatBuffer(char* s, size_t n, const char* format, Args&&... args) {
|
|
|
- StringFormat f;
|
|
|
- size_t total = 0;
|
|
|
- if constexpr(sizeof...(args) > 0) {
|
|
|
- formatR(format, s, n, total, Core::forward<Args>(args)...);
|
|
|
- }
|
|
|
- return total + copyFormatUntil(format, s, n, f);
|
|
|
- }
|
|
|
-
|
|
|
- struct StringBase {
|
|
|
+ class StringBase {
|
|
|
+ public:
|
|
|
size_t index = 0;
|
|
|
size_t capacity = 0;
|
|
|
char* buffer = nullptr;
|
|
|
|
|
|
+ public:
|
|
|
StringBase() {
|
|
|
}
|
|
|
|
|
|
+ StringBase(char* s, size_t n) : capacity(n), buffer(s) {
|
|
|
+ }
|
|
|
+
|
|
|
StringBase(StringBase&& other) = default;
|
|
|
StringBase(const StringBase& other) = delete;
|
|
|
StringBase& operator=(StringBase&& other) = default;
|
|
|
StringBase& operator=(const StringBase& other) = delete;
|
|
|
|
|
|
+ operator const char*() const {
|
|
|
+ return buffer;
|
|
|
+ }
|
|
|
+
|
|
|
+ void clear() {
|
|
|
+ index = 0;
|
|
|
+ if(buffer != nullptr) {
|
|
|
+ *buffer = '\0';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t getCapacity() const {
|
|
|
+ return index >= capacity ? 0 : capacity - index;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t getTotal() const {
|
|
|
+ return index;
|
|
|
+ }
|
|
|
+
|
|
|
+ char* getCurrent() const {
|
|
|
+ return buffer + min(index, capacity);
|
|
|
+ }
|
|
|
+
|
|
|
+ template<typename... Args>
|
|
|
+ size_t addFormat(const char* format, Args&&... args) {
|
|
|
+ size_t oldIndex = index;
|
|
|
+ (
|
|
|
+ [&] {
|
|
|
+ StringFormat f;
|
|
|
+ copyFormatUntil(format, f);
|
|
|
+ switcher(args, f);
|
|
|
+ }(),
|
|
|
+ ...);
|
|
|
+
|
|
|
+ while(*format != '\0') {
|
|
|
+ StringFormat f;
|
|
|
+ copyFormatUntil(format, f);
|
|
|
+ }
|
|
|
+ return index - oldIndex;
|
|
|
+ }
|
|
|
+
|
|
|
template<typename... Args>
|
|
|
size_t format(const char* format, Args&&... args) {
|
|
|
- size_t total = formatBuffer(
|
|
|
- buffer + index, capacity - index, format,
|
|
|
- Core::forward<Args>(args)...);
|
|
|
- index = Core::min(index + total, capacity);
|
|
|
- return total;
|
|
|
+ clear();
|
|
|
+ return addFormat<Args...>(format, Core::forward<Args>(args)...);
|
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
|
size_t add(const T& t) {
|
|
|
- size_t total = toString(t, buffer + index, capacity - index);
|
|
|
- index = Core::min(index + total, capacity);
|
|
|
- return total;
|
|
|
+ size_t oldIndex = index;
|
|
|
+ switcher(t);
|
|
|
+ return index - oldIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ void print() const;
|
|
|
+
|
|
|
+ private:
|
|
|
+ void copyFormatUntil(const char*& format, StringFormat& f);
|
|
|
+
|
|
|
+#define TO_STRING(type) void toString(type t, const StringFormat& format)
|
|
|
+
|
|
|
+ TO_STRING(signed char);
|
|
|
+ TO_STRING(char);
|
|
|
+ TO_STRING(short);
|
|
|
+ TO_STRING(int);
|
|
|
+ TO_STRING(long);
|
|
|
+ TO_STRING(long long);
|
|
|
+ TO_STRING(unsigned char);
|
|
|
+ TO_STRING(unsigned short);
|
|
|
+ TO_STRING(unsigned int);
|
|
|
+ TO_STRING(unsigned long);
|
|
|
+ TO_STRING(unsigned long long);
|
|
|
+ TO_STRING(float);
|
|
|
+ TO_STRING(double);
|
|
|
+ TO_STRING(const char*);
|
|
|
+ TO_STRING(const unsigned char*);
|
|
|
+ TO_STRING(unsigned char*);
|
|
|
+ TO_STRING(bool);
|
|
|
+
|
|
|
+ template<typename T>
|
|
|
+ void switcher(const T& t, const StringFormat& format = {}) {
|
|
|
+ size_t oldIndex = index;
|
|
|
+ if constexpr(Core::Iterable<T>) {
|
|
|
+ switcher("[");
|
|
|
+ auto current = t.begin();
|
|
|
+ auto end = t.end();
|
|
|
+ if(current != end) {
|
|
|
+ switcher(*current);
|
|
|
+ ++current;
|
|
|
+ }
|
|
|
+ while(current != end) {
|
|
|
+ switcher(", ");
|
|
|
+ switcher(*current);
|
|
|
+ ++current;
|
|
|
+ }
|
|
|
+ switcher("]");
|
|
|
+ } else if constexpr(ToString<T>) {
|
|
|
+ t.toString(*this);
|
|
|
+ } else {
|
|
|
+ toString(t, format);
|
|
|
+ }
|
|
|
+ applyPostFormat(oldIndex, index - oldIndex, format);
|
|
|
}
|
|
|
|
|
|
- void print();
|
|
|
+ void applyPostFormat(
|
|
|
+ size_t startIndex, size_t length, const StringFormat& format);
|
|
|
+ void insertSpace(size_t startIndex);
|
|
|
};
|
|
|
|
|
|
template<size_t N>
|
|
|
struct String : public StringBase {
|
|
|
char data[N]{};
|
|
|
|
|
|
- String() {
|
|
|
- buffer = data;
|
|
|
- capacity = N;
|
|
|
+ String() : StringBase(data, N) {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
template<typename... Args>
|
|
|
void print(const char* format, Args&&... args) {
|
|
|
String<256> s;
|
|
|
- s.format(format, Core::forward<Args>(args)...);
|
|
|
+ s.addFormat(format, Core::forward<Args>(args)...);
|
|
|
s.print();
|
|
|
}
|
|
|
}
|