Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da0954ffcc | ||
|
|
f3344a0fb9 | ||
|
|
eb25e98f79 | ||
|
|
5b85feec19 | ||
|
|
c4e7fa3375 |
@@ -28,7 +28,7 @@ void Builder::putLiteral(std::string_view literal)
|
||||
|
||||
void Builder::putF32(float number, size_t precision) const
|
||||
{
|
||||
precision = (precision > std::numeric_limits<float>::digits10) ? m_precision : precision;
|
||||
precision = (precision > std::numeric_limits<float>::digits10) ? 6 : precision;
|
||||
|
||||
std::stringstream stream;
|
||||
stream
|
||||
@@ -41,7 +41,7 @@ void Builder::putF32(float number, size_t precision) const
|
||||
|
||||
void Builder::putF64(double number, size_t precision) const
|
||||
{
|
||||
precision = (precision > std::numeric_limits<double>::digits10) ? m_precision : precision;
|
||||
precision = (precision > std::numeric_limits<double>::digits10) ? 6 : precision;
|
||||
|
||||
std::stringstream stream;
|
||||
stream
|
||||
@@ -52,9 +52,4 @@ void Builder::putF64(double number, size_t precision) const
|
||||
m_builder << string;
|
||||
}
|
||||
|
||||
void Builder::resetSpecifiers()
|
||||
{
|
||||
setPrecision();
|
||||
}
|
||||
|
||||
} // namespace Util::Format
|
||||
|
||||
@@ -16,8 +16,7 @@ namespace Util::Format {
|
||||
class Builder {
|
||||
public:
|
||||
explicit Builder(std::stringstream& builder)
|
||||
: m_precision(6)
|
||||
, m_builder(builder)
|
||||
: m_builder(builder)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -27,20 +26,16 @@ public:
|
||||
void putU32(uint32_t number) const { m_builder << number; } // unsigned int
|
||||
void putI64(int64_t number) const { m_builder << number; } // long int
|
||||
void putU64(size_t number) const { m_builder << number; } // long unsigned int
|
||||
void putF32(float number, size_t precision = -1) const;
|
||||
void putF64(double number, size_t precision = -1) const;
|
||||
void putF32(float number, size_t precision = 6) const;
|
||||
void putF64(double number, size_t precision = 6) const;
|
||||
void putCharacter(char character) const { m_builder.write(&character, 1); }
|
||||
void putString(const std::string_view string) const { m_builder.write(string.data(), string.length()); }
|
||||
void putPointer(const void* pointer) const { m_builder << pointer; }
|
||||
|
||||
void resetSpecifiers();
|
||||
void setPrecision(size_t precision = 6) { m_precision = precision; };
|
||||
|
||||
const std::stringstream& builder() const { return m_builder; }
|
||||
std::stringstream& builder() { return m_builder; }
|
||||
|
||||
private:
|
||||
size_t m_precision { 6 };
|
||||
std::stringstream& m_builder;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,27 +11,36 @@
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/format.h"
|
||||
#include "util/format/parser.h"
|
||||
#include "util/meta/assert.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters)
|
||||
{
|
||||
// Consume everything until '{' or EOF
|
||||
const auto literal = parser.consumeLiteral();
|
||||
builder.putLiteral(literal);
|
||||
|
||||
if (!parameters.isEOF()) {
|
||||
std::string_view specifier;
|
||||
if (parser.consumeSpecifier(specifier)) {
|
||||
parser.applySpecifier(builder, specifier);
|
||||
// Reached end of format string
|
||||
if (parser.isEOF()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& parameter = parameters.parameter(parameters.tell());
|
||||
parameter.format(builder, parameter.value);
|
||||
// Consume index + ':'
|
||||
auto indexMaybe = parser.consumeIndex();
|
||||
|
||||
// Get parameter at index, or next
|
||||
size_t index = indexMaybe.has_value() ? indexMaybe.value() : parameters.tell();
|
||||
VERIFY(index < parameters.size(), "argument not found at index '%zu':'%zu'", index, parameters.size());
|
||||
auto& parameter = parameters.parameter(index);
|
||||
|
||||
// Format the parameter
|
||||
parameter.format(builder, parser, parameter.value);
|
||||
|
||||
// Go to next parameter
|
||||
parameters.ignore();
|
||||
|
||||
builder.resetSpecifiers();
|
||||
}
|
||||
|
||||
// Recurse
|
||||
if (!parser.isEOF()) {
|
||||
variadicFormatImpl(builder, parser, parameters);
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/parser.h"
|
||||
#include "util/format/toformat.h"
|
||||
#include "util/format/formatter.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
class Builder;
|
||||
class Parser;
|
||||
|
||||
struct Parameter {
|
||||
const void* value;
|
||||
void (*format)(Builder& builder, const void* value);
|
||||
@@ -26,7 +27,8 @@ struct Parameter {
|
||||
template<typename T>
|
||||
void formatParameterValue(Builder& builder, const void* value)
|
||||
{
|
||||
_format(builder, *static_cast<const T*>(value));
|
||||
Formatter<T> formatter;
|
||||
formatter.format(builder, *static_cast<const T*>(value));
|
||||
}
|
||||
|
||||
// Type erasure improves both compile time and binary size significantly
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int32_t, uint32_t, int64_t,
|
||||
#include <cstring> // strlen
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/formatter.h"
|
||||
#include "util/format/parser.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
// Integral
|
||||
|
||||
template<>
|
||||
void Formatter<int32_t>::format(Builder& builder, int32_t value) const
|
||||
{
|
||||
builder.putI32(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<uint32_t>::format(Builder& builder, uint32_t value) const
|
||||
{
|
||||
builder.putU32(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<int64_t>::format(Builder& builder, int64_t value) const
|
||||
{
|
||||
builder.putI64(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<size_t>::format(Builder& builder, size_t value) const
|
||||
{
|
||||
builder.putU64(value);
|
||||
}
|
||||
|
||||
// Floating point
|
||||
|
||||
template<>
|
||||
void Formatter<float>::format(Builder& builder, float value) const
|
||||
{
|
||||
builder.putF32(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<double>::format(Builder& builder, double value) const
|
||||
{
|
||||
builder.putF64(value);
|
||||
}
|
||||
|
||||
// Char
|
||||
|
||||
template<>
|
||||
void Formatter<char>::format(Builder& builder, char value) const
|
||||
{
|
||||
builder.putCharacter(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<bool>::format(Builder& builder, bool value) const
|
||||
{
|
||||
builder.putString(value ? "true" : "false");
|
||||
}
|
||||
|
||||
// String
|
||||
|
||||
void Formatter<const char*>::format(Builder& builder, const char* value) const
|
||||
{
|
||||
builder.putString(value != nullptr ? std::string_view { value, strlen(value) } : "(nil)");
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<std::string_view>::format(Builder& builder, std::string_view value) const
|
||||
{
|
||||
builder.putString(value);
|
||||
}
|
||||
|
||||
// Pointer
|
||||
|
||||
void Formatter<std::nullptr_t>::format(Builder& builder, std::nullptr_t) const
|
||||
{
|
||||
Formatter<const void*>::format(builder, 0);
|
||||
}
|
||||
|
||||
} // namespace Util::Format
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int32_t, uint8_t, uint32_t, int64_t,
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/parser.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct Formatter {
|
||||
|
||||
void format(Builder& builder, T value) const { (void)builder, (void)value; }
|
||||
};
|
||||
|
||||
// Integral
|
||||
|
||||
template<>
|
||||
void Formatter<int32_t>::format(Builder& builder, int32_t value) const;
|
||||
|
||||
template<>
|
||||
void Formatter<uint32_t>::format(Builder& builder, uint32_t value) const;
|
||||
|
||||
template<>
|
||||
void Formatter<int64_t>::format(Builder& builder, int64_t value) const;
|
||||
|
||||
template<>
|
||||
void Formatter<size_t>::format(Builder& builder, size_t value) const; // uint64_t
|
||||
|
||||
// Floating point
|
||||
|
||||
template<>
|
||||
void Formatter<float>::format(Builder& builder, float value) const;
|
||||
|
||||
template<>
|
||||
void Formatter<double>::format(Builder& builder, double value) const;
|
||||
|
||||
// Char
|
||||
|
||||
template<>
|
||||
void Formatter<char>::format(Builder& builder, char value) const;
|
||||
|
||||
template<>
|
||||
void Formatter<bool>::format(Builder& builder, bool value) const;
|
||||
|
||||
// String
|
||||
|
||||
template<>
|
||||
void Formatter<std::string_view>::format(Builder& builder, std::string_view value) const;
|
||||
|
||||
template<>
|
||||
struct Formatter<std::string> : Formatter<std::string_view> {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Formatter<const char*> : Formatter<std::string_view> {
|
||||
void format(Builder& builder, const char* value) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Formatter<char*> : Formatter<const char*> {
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct Formatter<char[N]> : Formatter<const char*> {
|
||||
};
|
||||
|
||||
// Pointer
|
||||
|
||||
template<typename T>
|
||||
struct Formatter<T*> {
|
||||
void format(Builder& builder, T* value) const
|
||||
{
|
||||
value == nullptr
|
||||
? builder.putString("0x0")
|
||||
: builder.putPointer(static_cast<const void*>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Formatter<std::nullptr_t> : Formatter<const void*> {
|
||||
void format(Builder& builder, std::nullptr_t) const;
|
||||
};
|
||||
|
||||
// Container
|
||||
|
||||
template<typename T>
|
||||
struct Formatter<std::vector<T>> : Formatter<T> {
|
||||
void format(Builder& builder, const std::vector<T>& value) const
|
||||
{
|
||||
builder.putString("{\n");
|
||||
for (auto it = value.cbegin(); it != value.cend(); ++it) {
|
||||
builder.putString(" ");
|
||||
Formatter<T>::format(builder, *it);
|
||||
|
||||
// Add comma, except after the last element
|
||||
if (it != std::prev(value.end(), 1)) {
|
||||
builder.putCharacter(',');
|
||||
}
|
||||
builder.putCharacter('\n');
|
||||
}
|
||||
builder.putCharacter('}');
|
||||
}
|
||||
};
|
||||
|
||||
#define UTIL_FORMAT_FORMAT_AS_MAP(type) \
|
||||
template<typename K, typename V> \
|
||||
struct Formatter<type<K, V>> \
|
||||
: Formatter<K> \
|
||||
, Formatter<V> { \
|
||||
void format(Builder& builder, const type<K, V>& value) const \
|
||||
{ \
|
||||
builder.putString("{\n"); \
|
||||
auto last = value.end(); \
|
||||
for (auto it = value.begin(); it != last; ++it) { \
|
||||
builder.putString(R"( ")"); \
|
||||
Formatter<K>::format(builder, it->first); \
|
||||
builder.putString(R"(": )"); \
|
||||
Formatter<V>::format(builder, it->second); \
|
||||
\
|
||||
/* Add comma, except after the last element */ \
|
||||
if (std::next(it) != last) { \
|
||||
builder.putCharacter(','); \
|
||||
} \
|
||||
\
|
||||
builder.putCharacter('\n'); \
|
||||
} \
|
||||
builder.putCharacter('}'); \
|
||||
} \
|
||||
}
|
||||
|
||||
UTIL_FORMAT_FORMAT_AS_MAP(std::map);
|
||||
UTIL_FORMAT_FORMAT_AS_MAP(std::unordered_map);
|
||||
} // namespace Util::Format
|
||||
+16
-16
@@ -17,31 +17,31 @@ std::string formatTimeElapsed()
|
||||
return format("{:.3}s ", s_timer.elapsedNanoseconds() / 1000000000.0);
|
||||
}
|
||||
|
||||
std::string formatType(Type type)
|
||||
std::string formatType(LogType type)
|
||||
{
|
||||
std::string output;
|
||||
|
||||
formatTo(output, "[");
|
||||
switch (type) {
|
||||
case Type::Trace:
|
||||
case LogType::Trace:
|
||||
formatTo(output, "trace");
|
||||
break;
|
||||
case Type::Debug:
|
||||
case LogType::Debug:
|
||||
formatTo(output, fg(TerminalColor::Magenta), "debug");
|
||||
break;
|
||||
case Type::Success:
|
||||
case LogType::Success:
|
||||
formatTo(output, fg(TerminalColor::Green), "success");
|
||||
break;
|
||||
case Type::Info:
|
||||
case LogType::Info:
|
||||
formatTo(output, fg(TerminalColor::Blue), "info");
|
||||
break;
|
||||
case Type::Warn:
|
||||
case LogType::Warn:
|
||||
formatTo(output, Emphasis::Bold | fg(TerminalColor::Yellow), "warn");
|
||||
break;
|
||||
case Type::Error:
|
||||
case LogType::Error:
|
||||
formatTo(output, Emphasis::Bold | fg(TerminalColor::Red), "error");
|
||||
break;
|
||||
case Type::Critical:
|
||||
case LogType::Critical:
|
||||
formatTo(output, Emphasis::Bold | fg(TerminalColor::White) | bg(TerminalColor::Red), "critical");
|
||||
break;
|
||||
default:
|
||||
@@ -54,7 +54,7 @@ std::string formatType(Type type)
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
LogOperatorStyle::LogOperatorStyle(FILE* file, Type type)
|
||||
LogOperatorStyle::LogOperatorStyle(FILE* file, LogType type)
|
||||
: m_file(file)
|
||||
, m_type(type)
|
||||
, m_stream()
|
||||
@@ -73,37 +73,37 @@ LogOperatorStyle::~LogOperatorStyle()
|
||||
|
||||
LogOperatorStyle trace()
|
||||
{
|
||||
return LogOperatorStyle(stdout, Type::Trace);
|
||||
return LogOperatorStyle(stdout, LogType::Trace);
|
||||
}
|
||||
|
||||
LogOperatorStyle debug()
|
||||
{
|
||||
return LogOperatorStyle(stdout, Type::Debug);
|
||||
return LogOperatorStyle(stdout, LogType::Debug);
|
||||
}
|
||||
|
||||
LogOperatorStyle success()
|
||||
{
|
||||
return LogOperatorStyle(stdout, Type::Success);
|
||||
return LogOperatorStyle(stdout, LogType::Success);
|
||||
}
|
||||
|
||||
LogOperatorStyle info()
|
||||
{
|
||||
return LogOperatorStyle(stdout, Type::Info);
|
||||
return LogOperatorStyle(stdout, LogType::Info);
|
||||
}
|
||||
|
||||
LogOperatorStyle warn()
|
||||
{
|
||||
return LogOperatorStyle(stderr, Type::Warn);
|
||||
return LogOperatorStyle(stderr, LogType::Warn);
|
||||
}
|
||||
|
||||
LogOperatorStyle error()
|
||||
{
|
||||
return LogOperatorStyle(stderr, Type::Error);
|
||||
return LogOperatorStyle(stderr, LogType::Error);
|
||||
}
|
||||
|
||||
LogOperatorStyle critical()
|
||||
{
|
||||
return LogOperatorStyle(stderr, Type::Critical);
|
||||
return LogOperatorStyle(stderr, LogType::Critical);
|
||||
}
|
||||
|
||||
} // namespace Util::Format
|
||||
|
||||
+11
-11
@@ -18,7 +18,7 @@ namespace Util::Format {
|
||||
|
||||
static Util::Timer s_timer;
|
||||
|
||||
enum class Type : uint8_t {
|
||||
enum class LogType : uint8_t {
|
||||
Trace, // White
|
||||
Debug, // Purple
|
||||
Success, // Green
|
||||
@@ -29,7 +29,7 @@ enum class Type : uint8_t {
|
||||
};
|
||||
|
||||
std::string formatTimeElapsed();
|
||||
std::string formatType(Type type);
|
||||
std::string formatType(LogType type);
|
||||
|
||||
#define LOG_FUNCTION(name, file, type) \
|
||||
template<size_t N, typename... Parameters> \
|
||||
@@ -42,26 +42,26 @@ std::string formatType(Type type);
|
||||
print(file, "\n"); \
|
||||
}
|
||||
|
||||
LOG_FUNCTION(trace, stdout, Type::Trace);
|
||||
LOG_FUNCTION(debug, stdout, Type::Debug);
|
||||
LOG_FUNCTION(success, stdout, Type::Success);
|
||||
LOG_FUNCTION(info, stdout, Type::Info);
|
||||
LOG_FUNCTION(warn, stderr, Type::Warn);
|
||||
LOG_FUNCTION(error, stderr, Type::Error);
|
||||
LOG_FUNCTION(critical, stderr, Type::Critical);
|
||||
LOG_FUNCTION(trace, stdout, LogType::Trace);
|
||||
LOG_FUNCTION(debug, stdout, LogType::Debug);
|
||||
LOG_FUNCTION(success, stdout, LogType::Success);
|
||||
LOG_FUNCTION(info, stdout, LogType::Info);
|
||||
LOG_FUNCTION(warn, stderr, LogType::Warn);
|
||||
LOG_FUNCTION(error, stderr, LogType::Error);
|
||||
LOG_FUNCTION(critical, stderr, LogType::Critical);
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class LogOperatorStyle {
|
||||
public:
|
||||
LogOperatorStyle(FILE* file, Type type);
|
||||
LogOperatorStyle(FILE* file, LogType type);
|
||||
virtual ~LogOperatorStyle();
|
||||
|
||||
Builder& builder() { return m_builder; }
|
||||
|
||||
private:
|
||||
FILE* m_file;
|
||||
Type m_type;
|
||||
LogType m_type;
|
||||
|
||||
std::stringstream m_stream;
|
||||
Builder m_builder;
|
||||
|
||||
+47
-25
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <algorithm> // replace
|
||||
#include <cstddef> // size_t
|
||||
#include <limits> // numeric_limits
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -53,7 +54,9 @@ void Parser::checkFormatParameterConsistency()
|
||||
if (peek0 == '{') {
|
||||
braceOpen++;
|
||||
|
||||
VERIFY(peek1 == '}' || peek1 == ':', "invalid parameter reference");
|
||||
if (peek1 >= '0' && peek1 <= '9') {
|
||||
m_mode = ArgumentIndexingMode::Manual;
|
||||
}
|
||||
}
|
||||
if (peek0 == '}') {
|
||||
braceClose++;
|
||||
@@ -63,13 +66,13 @@ void Parser::checkFormatParameterConsistency()
|
||||
}
|
||||
m_index = 0;
|
||||
|
||||
VERIFY(!(braceOpen < braceClose), "extra open braces in format string");
|
||||
// VERIFY(!(braceOpen < braceClose), "extra open braces in format string");
|
||||
|
||||
VERIFY(!(braceOpen > braceClose), "extra closing braces in format string");
|
||||
// VERIFY(!(braceOpen > braceClose), "extra closing braces in format string");
|
||||
|
||||
VERIFY(!(braceOpen < m_parameterCount), "format string does not reference all passed parameters");
|
||||
// VERIFY(!(braceOpen < m_parameterCount), "format string does not reference all passed parameters");
|
||||
|
||||
VERIFY(!(braceOpen > m_parameterCount), "format string references nonexistent parameter");
|
||||
// VERIFY(!(braceOpen > m_parameterCount), "format string references nonexistent parameter");
|
||||
}
|
||||
|
||||
std::string_view Parser::consumeLiteral()
|
||||
@@ -101,44 +104,63 @@ std::string_view Parser::consumeLiteral()
|
||||
return m_input.substr(begin);
|
||||
}
|
||||
|
||||
bool Parser::consumeSpecifier(std::string_view& specifier)
|
||||
std::optional<size_t> Parser::consumeIndex()
|
||||
{
|
||||
if (!consumeSpecific('{')) {
|
||||
return false;
|
||||
VERIFY_NOT_REACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!consumeSpecific(':')) {
|
||||
VERIFY(consumeSpecific('}'), "invalid parameter reference");
|
||||
specifier = "";
|
||||
switch (m_mode) {
|
||||
case ArgumentIndexingMode::Automatic: {
|
||||
VERIFY(consumeSpecific('}') || consumeSpecific(':'), "expecting '}' or ':', not '%c'", peek());
|
||||
return {};
|
||||
}
|
||||
else {
|
||||
case ArgumentIndexingMode::Manual: {
|
||||
const auto begin = tell();
|
||||
|
||||
// Go until AFTER the closing brace
|
||||
while (peek(-1) != '}') {
|
||||
consume();
|
||||
while (!isEOF()) {
|
||||
char peek0 = peek();
|
||||
if (peek0 == '}' || peek0 == ':') {
|
||||
break;
|
||||
}
|
||||
|
||||
specifier = m_input.substr(begin, tell() - begin - 1);
|
||||
VERIFY(peek0 >= '0' && peek0 <= '9', "expecting number, not '%c'", peek0);
|
||||
|
||||
ignore();
|
||||
}
|
||||
|
||||
return true;
|
||||
size_t result = stringToNumber(m_input.substr(begin, tell() - begin));
|
||||
|
||||
if (peek() == ':') {
|
||||
ignore();
|
||||
}
|
||||
|
||||
void Parser::applySpecifier(Builder& builder, std::string_view specifier)
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
size_t Parser::stringToNumber(std::string_view value)
|
||||
{
|
||||
if (specifier[0] == '.') {
|
||||
size_t value = 0;
|
||||
size_t result = 0;
|
||||
|
||||
for (size_t i = 1; i < value.length(); ++i) {
|
||||
if (value[i] < '0' || value[i] > '9') {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
result *= 10;
|
||||
result += value[i] - '0'; // Subtract ASCII 48 to get the number
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < specifier.length(); ++i) {
|
||||
if (specifier[i] < '0' || specifier[i] > '9') {
|
||||
return;
|
||||
}
|
||||
value *= 10;
|
||||
value += specifier[i] - '0'; // Subtract ASCII 48 to get the number
|
||||
}
|
||||
|
||||
builder.setPrecision(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/genericlexer.h"
|
||||
@@ -17,17 +18,24 @@ class Builder;
|
||||
|
||||
class Parser final : public GenericLexer {
|
||||
public:
|
||||
enum class ArgumentIndexingMode {
|
||||
Automatic, // {} ,{}
|
||||
Manual, // {0},{1}
|
||||
};
|
||||
|
||||
Parser(std::string_view format, size_t parameterCount);
|
||||
virtual ~Parser();
|
||||
|
||||
void checkFormatParameterConsistency();
|
||||
|
||||
std::string_view consumeLiteral();
|
||||
bool consumeSpecifier(std::string_view& specifier);
|
||||
std::optional<size_t> consumeIndex();
|
||||
|
||||
size_t stringToNumber(std::string_view value);
|
||||
|
||||
void applySpecifier(Builder& builder, std::string_view specifier);
|
||||
|
||||
private:
|
||||
ArgumentIndexingMode m_mode { ArgumentIndexingMode::Automatic };
|
||||
size_t m_parameterCount { 0 };
|
||||
};
|
||||
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // nullptr_t, size_t
|
||||
#include <cstdint> // int32_t, uint32_t, int64_t
|
||||
#include <cstring> // strlen
|
||||
#include <iterator> // next
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility> // forward
|
||||
#include <vector>
|
||||
|
||||
#include "util/meta/odr.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, std::nullptr_t)
|
||||
{
|
||||
builder.putString("(nil)");
|
||||
}
|
||||
|
||||
template<typename FormatBuilder, typename T>
|
||||
void format(FormatBuilder& builder, T* pointer)
|
||||
{
|
||||
builder.putPointer(static_cast<const void*>(pointer));
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, bool boolean)
|
||||
{
|
||||
builder.putString(boolean ? "true" : "false");
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, int32_t number) // int
|
||||
{
|
||||
builder.putI32(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, uint32_t number) // unsigned int
|
||||
{
|
||||
builder.putU32(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, int64_t number) // long int
|
||||
{
|
||||
builder.putI64(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder> // uint64_t
|
||||
void format(FormatBuilder& builder, size_t number) // long unsigned int
|
||||
{
|
||||
builder.putU64(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, float number)
|
||||
{
|
||||
builder.putF32(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, double number)
|
||||
{
|
||||
builder.putF64(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, char character)
|
||||
{
|
||||
builder.putCharacter(character);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, const char* string)
|
||||
{
|
||||
builder.putString({ string, strlen(string) });
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, const std::string& string)
|
||||
{
|
||||
builder.putString(string);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, std::string_view string)
|
||||
{
|
||||
builder.putString(string);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder, typename T>
|
||||
void format(FormatBuilder& builder, const std::vector<T>& array)
|
||||
{
|
||||
builder.putString("{\n");
|
||||
for (auto it = array.cbegin(); it != array.cend(); ++it) {
|
||||
builder.putString(" ");
|
||||
format(builder, *it);
|
||||
|
||||
// Add comma, except after the last element
|
||||
if (it != std::prev(array.end(), 1)) {
|
||||
builder.putCharacter(',');
|
||||
}
|
||||
builder.putCharacter('\n');
|
||||
}
|
||||
builder.putCharacter('}');
|
||||
}
|
||||
|
||||
#define FORMAT_MAP \
|
||||
builder.putString("{\n"); \
|
||||
auto last = map.end(); \
|
||||
for (auto it = map.begin(); it != last; ++it) { \
|
||||
builder.putString(R"( ")"); \
|
||||
format(builder, it->first); \
|
||||
builder.putString(R"(": )"); \
|
||||
format(builder, it->second); \
|
||||
\
|
||||
/* Add comma, except after the last element */ \
|
||||
if (std::next(it) != last) { \
|
||||
builder.putCharacter(','); \
|
||||
} \
|
||||
\
|
||||
builder.putCharacter('\n'); \
|
||||
} \
|
||||
builder.putCharacter('}');
|
||||
|
||||
template<typename FormatBuilder, typename K, typename V>
|
||||
void format(FormatBuilder& builder, const std::map<K, V>& map)
|
||||
{
|
||||
FORMAT_MAP;
|
||||
}
|
||||
|
||||
template<typename FormatBuilder, typename K, typename V>
|
||||
void format(FormatBuilder& builder, const std::unordered_map<K, V>& map)
|
||||
{
|
||||
FORMAT_MAP;
|
||||
}
|
||||
|
||||
struct formatFunction {
|
||||
template<typename FormatBuilder, typename T>
|
||||
auto operator()(FormatBuilder& builder, T&& value) const
|
||||
{
|
||||
return format(builder, std::forward<T>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
namespace {
|
||||
constexpr const auto& _format = Util::Detail::staticConst<Detail::formatFunction>; // NOLINT(misc-definitions-in-headers,clang-diagnostic-unused-variable)
|
||||
} // namespace
|
||||
|
||||
} // namespace Util::Format
|
||||
@@ -66,8 +66,8 @@ inline void __assertion_failed(const char* assertion, const char* file, uint32_t
|
||||
fprintf(stderr, ": ");
|
||||
// Cant use the formatting library to print asserts caused by the formatting library
|
||||
std::string_view functionString = function;
|
||||
if (!functionString.starts_with("Util::Format::")
|
||||
&& !functionString.starts_with("Util::GenericLexer::")) {
|
||||
if (functionString.find("Util::Format::") != std::string_view::npos
|
||||
&& functionString.find("Util::GenericLexer::") != std::string_view::npos) {
|
||||
std::string message;
|
||||
formatTo(message, parameters...);
|
||||
fprintf(stderr, "%s", message.c_str());
|
||||
|
||||
Reference in New Issue
Block a user