Compare commits
35
Commits
1361ee04f9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
243442a3b6 | ||
|
|
d5bf50715e | ||
|
|
8520952235 | ||
|
|
526948874a | ||
|
|
c8e4ae884e | ||
|
|
07c9f9959d | ||
|
|
9000d1d968 | ||
|
|
50fe09ee56 | ||
|
|
6cd2fe4e9a | ||
|
|
703be90792 | ||
|
|
da9fb1ed0f | ||
|
|
d6f378b6bc | ||
|
|
e592ee7fd4 | ||
|
|
8e2e1a13df | ||
|
|
afdd0f8b45 | ||
|
|
93af096d68 | ||
|
|
228e290b94 | ||
|
|
cacd3ca8fd | ||
|
|
953df138c0 | ||
|
|
f3e49d8f74 | ||
|
|
3b7d8d1e4a | ||
|
|
d3de1fb635 | ||
|
|
f31f7feb5e | ||
|
|
7093b5ad3d | ||
|
|
145c7e44a2 | ||
|
|
29406fac1f | ||
|
|
27489c6cb4 | ||
|
|
a83615083d | ||
|
|
febb89be9c | ||
|
|
bbeab2efd0 | ||
|
|
0448e01f0a | ||
|
|
94eb1dd173 | ||
|
|
f7157feddf | ||
|
|
b5e55f13d3 | ||
|
|
054980e0f2 |
@@ -14,6 +14,7 @@ AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
||||
+13
-11
@@ -4,8 +4,13 @@
|
||||
# Set project name
|
||||
set(PROJECT "ruc")
|
||||
|
||||
# Unit tests
|
||||
option(RUC_BUILD_TESTS "Build the RUC test programs" ON)
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(RUC_STANDALONE TRUE)
|
||||
endif()
|
||||
|
||||
# Options
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
option(RUC_BUILD_TESTS "Build the RUC test programs" ${RUC_STANDALONE})
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
@@ -55,8 +60,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
# Define library source files
|
||||
file(GLOB_RECURSE LIBRARY_SOURCES "src/*.cpp")
|
||||
|
||||
add_library(${PROJECT} STATIC ${LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT} PRIVATE
|
||||
add_library(${PROJECT} ${LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT} PUBLIC
|
||||
"src")
|
||||
|
||||
# ------------------------------------------
|
||||
@@ -65,8 +70,8 @@ target_include_directories(${PROJECT} PRIVATE
|
||||
# Define source files
|
||||
file(GLOB TEST_LIBRARY_SOURCES "test/*.cpp" "src/ruc/timer.cpp")
|
||||
|
||||
add_library(${PROJECT}-test STATIC ${TEST_LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT}-test PRIVATE
|
||||
add_library(${PROJECT}-test ${TEST_LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT}-test PUBLIC
|
||||
"src"
|
||||
"test")
|
||||
|
||||
@@ -75,11 +80,8 @@ target_include_directories(${PROJECT}-test PRIVATE
|
||||
|
||||
if (RUC_BUILD_TESTS)
|
||||
# Define test source files
|
||||
file(GLOB_RECURSE TEST_SOURCES "test/*.cpp")
|
||||
set(TEST_SOURCES ${TEST_SOURCES} ${LIBRARY_SOURCES})
|
||||
file(GLOB_RECURSE TEST_SOURCES "test/unit/*.cpp")
|
||||
|
||||
add_executable(${PROJECT}-unit-test ${TEST_SOURCES})
|
||||
target_include_directories(${PROJECT}-unit-test PRIVATE
|
||||
"src"
|
||||
"test")
|
||||
target_link_libraries(${PROJECT}-unit-test ruc ruc-test)
|
||||
endif()
|
||||
|
||||
@@ -9,3 +9,9 @@ Utility library for C++.
|
||||
- Formatting library
|
||||
- JSON parsing
|
||||
- Unit test macros
|
||||
|
||||
** Formatting library
|
||||
|
||||
The format specification has been heavily inspired by the ~{fmt}~ library, most of the [[https://fmt.dev/latest/syntax.html#format-specification-mini-language][mini-language]] is supported.
|
||||
|
||||
*Note*: Development was previously done at: [[https://github.com/riyyi/manafiles/tree/6f0e3d6063ab75ad81899135689569e440ddb813/src/util][manafiles/src/util]] and [[https://github.com/riyyi/manafiles/tree/6f0e3d6063ab75ad81899135689569e440ddb813/test][manafiles/test]].
|
||||
|
||||
+26
-11
@@ -7,22 +7,37 @@
|
||||
|
||||
b="$(tput bold)"
|
||||
red="$(tput setf 4)"
|
||||
yellow="$(tput setf 6)"
|
||||
blue="$(tput setf 1)"
|
||||
green="$(tput setf 2)"
|
||||
n="$(tput sgr0)"
|
||||
|
||||
if [ ! -d ".git" ]; then
|
||||
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
||||
exit 1
|
||||
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Temporary clear unstaged files from the repository to pass the diff checker
|
||||
unstaged="$(git --no-pager diff --name-only)"
|
||||
if [ -n "$unstaged" ]; then
|
||||
patch=".git/patch-$(date +%s)"
|
||||
echo "[${yellow}WARNING${n}]: Stashing unstaged files to $patch"
|
||||
git --no-pager diff > "$patch"
|
||||
git restore .
|
||||
|
||||
remove() {
|
||||
git apply "$patch"
|
||||
rm -rf "${patch:?}"
|
||||
echo "[${blue}INFO${n}]: Restored changes from $patch"
|
||||
}
|
||||
trap remove EXIT HUP INT TERM
|
||||
fi
|
||||
|
||||
# Get the path from the project root to the script
|
||||
subDir="$(dirname -- "$0")"
|
||||
|
||||
# Get all files staged for commit
|
||||
files="$(git --no-pager diff --cached --name-only)"
|
||||
|
||||
green="$(tput setf 2)"
|
||||
red="$(tput setf 4)"
|
||||
nc="$(tput sgr0)"
|
||||
files="$(git --no-pager diff --cached --name-only --diff-filter=d)"
|
||||
|
||||
failures=0
|
||||
|
||||
@@ -33,9 +48,9 @@ lint-shell-script.sh
|
||||
for linter in $linters; do
|
||||
echo "Running $subDir/$linter"
|
||||
if "$subDir/$linter" "$files"; then
|
||||
echo "[${green}PASS${nc}]: $subDir/$linter"
|
||||
echo "[${green}PASS${n}]: $subDir/$linter"
|
||||
else
|
||||
echo "[${red}FAIL${nc}]: $subDir/$linter"
|
||||
echo "[${red}FAIL${n}]: $subDir/$linter"
|
||||
failures=$(( failures + 1 ))
|
||||
fi
|
||||
done
|
||||
@@ -43,9 +58,9 @@ done
|
||||
echo "Running $subDir/lint-clang-format.sh"
|
||||
# shellcheck disable=SC2086
|
||||
if "$subDir/lint-clang-format.sh" "$files" && git diff --exit-code $files; then
|
||||
echo "[${green}PASS${nc}]: $subDir/lint-clang-format.sh"
|
||||
echo "[${green}PASS${n}]: $subDir/lint-clang-format.sh"
|
||||
else
|
||||
echo "[${red}FAIL${nc}]: $subDir/lint-clang-format.sh"
|
||||
echo "[${red}FAIL${n}]: $subDir/lint-clang-format.sh"
|
||||
failures=$(( failures + 1 ))
|
||||
fi
|
||||
|
||||
|
||||
+13
-11
@@ -5,13 +5,17 @@
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
b="$(tput bold)"
|
||||
red="$(tput setf 4)"
|
||||
n="$(tput sgr0)"
|
||||
error() {
|
||||
b="$(tput bold)"
|
||||
red="$(tput setf 4)"
|
||||
n="$(tput sgr0)"
|
||||
|
||||
echo "${b}${red}Error:${n} $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ ! -d ".git" ]; then
|
||||
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
||||
exit 1
|
||||
error "please run this script from the project root"
|
||||
fi
|
||||
|
||||
formatter=false
|
||||
@@ -20,16 +24,14 @@ if command -v clang-format-11 >/dev/null 2>&1; then
|
||||
elif command -v clang-format >/dev/null 2>&1; then
|
||||
formatter="clang-format"
|
||||
if ! "$formatter" --version | awk '{ if (substr($NF, 1, index($NF, ".") - 1) < 11) exit 1; }'; then
|
||||
echo "You are using '$("${CLANG_FORMAT}" --version)', which appears to not be clang-format 11 or later."
|
||||
exit 1
|
||||
error "you are using '$("${formatter}" --version)', which appears to not be clang-format 11 or later."
|
||||
fi
|
||||
else
|
||||
echo "clang-format-11 is not available, but C++ files need linting!"
|
||||
echo "Either skip this script, or install clang-format-11."
|
||||
exit 1
|
||||
error "clang-format-11 is not available, but C++ files need linting!
|
||||
Either skip this script, or install clang-format-11."
|
||||
fi
|
||||
|
||||
files="${1:-$(git --no-pager diff --cached --name-only)}"
|
||||
files="${1:-$(git --no-pager diff --cached --name-only --diff-filter=d)}"
|
||||
files="$(echo "$files" | grep -E '\.(cpp|h)$')"
|
||||
|
||||
if [ -z "$files" ]; then
|
||||
|
||||
@@ -5,22 +5,25 @@
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
b="$(tput bold)"
|
||||
red="$(tput setf 4)"
|
||||
n="$(tput sgr0)"
|
||||
error() {
|
||||
b="$(tput bold)"
|
||||
red="$(tput setf 4)"
|
||||
n="$(tput sgr0)"
|
||||
|
||||
echo "${b}${red}Error:${n} $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ ! -d ".git" ]; then
|
||||
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
||||
exit 1
|
||||
error "please run this script from the project root"
|
||||
fi
|
||||
|
||||
if ! command -v shellcheck > /dev/null 2>&1; then
|
||||
echo "shellcheck is not available, but shell script files need linting!"
|
||||
echo "Either skip this script, or install shellcheck."
|
||||
exit 1
|
||||
error "shellcheck is not available, but shell-script files need linting!
|
||||
Either skip this script, or install shellcheck."
|
||||
fi
|
||||
|
||||
files="${1:-$(git --no-pager diff --cached --name-only)}"
|
||||
files="${1:-$(git --no-pager diff --cached --name-only --diff-filter=d)}"
|
||||
files="$(echo "$files" | grep -E '\.sh$')"
|
||||
|
||||
if [ -z "$files" ]; then
|
||||
|
||||
@@ -259,6 +259,11 @@ bool ArgParser::parseArgument(std::string_view argument)
|
||||
}
|
||||
|
||||
bool ArgParser::parse(int argc, const char* argv[])
|
||||
{
|
||||
return parse(argc, const_cast<char**>(argv));
|
||||
}
|
||||
|
||||
bool ArgParser::parse(int argc, char* argv[])
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
};
|
||||
|
||||
bool parse(int argc, const char* argv[]);
|
||||
bool parse(int argc, char* argv[]);
|
||||
|
||||
void addOption(Option&& option);
|
||||
void addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString);
|
||||
|
||||
+44
-13
@@ -6,27 +6,25 @@
|
||||
|
||||
#include <cstdint> // int32_t
|
||||
#include <filesystem>
|
||||
#include <fstream> // ifstream, ios, ofstream
|
||||
#include <memory> // make_unique
|
||||
#include <fstream> // std::ifstream, std::ofstream
|
||||
#include <memory> // std::make_shared, std::make_unique, std::shared_ptr
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "ruc/file.h"
|
||||
#include "ruc/meta/assert.h"
|
||||
|
||||
namespace ruc {
|
||||
|
||||
File::File(const std::string& path)
|
||||
File::File(std::string_view path)
|
||||
: m_path(path)
|
||||
{
|
||||
// Create input stream object and open file
|
||||
std::ifstream file(path, std::ios::in);
|
||||
std::ifstream file(path.data());
|
||||
VERIFY(file.is_open(), "failed to open file: '{}'", path);
|
||||
|
||||
// Get length of the file
|
||||
file.seekg(0, std::ios::end);
|
||||
int32_t size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
VERIFY(size != -1, "failed to read file length: '{}', path");
|
||||
int32_t size = File::length(path, file);
|
||||
|
||||
// Allocate memory filled with zeros
|
||||
auto buffer = std::make_unique<char[]>(size);
|
||||
@@ -44,15 +42,48 @@ File::~File()
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
File File::create(const std::string& path)
|
||||
File File::create(std::string_view path)
|
||||
{
|
||||
if (!std::filesystem::exists(path)) {
|
||||
std::ofstream { path };
|
||||
std::ofstream { path.data() };
|
||||
}
|
||||
|
||||
return File(path);
|
||||
}
|
||||
|
||||
int32_t File::length(std::string_view path, std::ifstream& file)
|
||||
{
|
||||
file.seekg(0, std::ios::end);
|
||||
int32_t length = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
VERIFY(length != -1, "failed to read file length: '{}'", path);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// FIXME: Deduplicate code with constructor, this broke binary files only
|
||||
std::shared_ptr<char[]> File::raw(std::string_view path)
|
||||
{
|
||||
// Create input stream object and open file
|
||||
std::ifstream file(path.data());
|
||||
VERIFY(file.is_open(), "failed to open file: '{}'", path);
|
||||
|
||||
// Get length of the file
|
||||
int32_t size = File::length(path, file);
|
||||
|
||||
// Allocate memory filled with zeros
|
||||
auto buffer = std::make_shared<char[]>(size + 1);
|
||||
|
||||
// Fill buffer with file contents
|
||||
file.read(buffer.get(), size);
|
||||
file.close();
|
||||
|
||||
// Null termination
|
||||
buffer[size] = '\0';
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void File::clear()
|
||||
@@ -60,14 +91,14 @@ void File::clear()
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
File& File::append(const std::string& data)
|
||||
File& File::append(std::string_view data)
|
||||
{
|
||||
m_data.append(data);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& File::replace(size_t index, size_t length, const std::string& data)
|
||||
File& File::replace(size_t index, size_t length, std::string_view data)
|
||||
{
|
||||
m_data.replace(index, length, data);
|
||||
|
||||
@@ -77,7 +108,7 @@ File& File::replace(size_t index, size_t length, const std::string& data)
|
||||
File& File::flush()
|
||||
{
|
||||
// Create output stream object and open file
|
||||
std::ofstream file(m_path, std::ios::out | std::ios::trunc);
|
||||
std::ofstream file(m_path.data(), std::ios::out | std::ios::trunc);
|
||||
VERIFY(file.is_open(), "failed to open file: '{}'", m_path);
|
||||
|
||||
// Write data to disk
|
||||
|
||||
+11
-6
@@ -6,28 +6,33 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // int32_t
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ruc {
|
||||
|
||||
class File {
|
||||
public:
|
||||
File(const std::string& path);
|
||||
File(std::string_view path);
|
||||
virtual ~File();
|
||||
|
||||
static File create(const std::string& path);
|
||||
static File create(std::string_view path);
|
||||
static int32_t length(std::string_view path, std::ifstream& file);
|
||||
static std::shared_ptr<char[]> raw(std::string_view path);
|
||||
|
||||
void clear();
|
||||
File& append(const std::string& data);
|
||||
File& replace(size_t index, size_t length, const std::string& data);
|
||||
File& append(std::string_view data);
|
||||
File& replace(size_t index, size_t length, std::string_view data);
|
||||
File& flush();
|
||||
|
||||
const char* c_str() const { return m_data.c_str(); }
|
||||
const std::string& data() const { return m_data; }
|
||||
const std::string& path() const { return m_path; }
|
||||
std::string_view path() const { return m_path; }
|
||||
|
||||
private:
|
||||
std::string m_path;
|
||||
std::string_view m_path;
|
||||
std::string m_data;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <algorithm> // min
|
||||
#include <charconv> // std::to_chars
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||
#include <iomanip> // setprecision
|
||||
@@ -178,15 +179,53 @@ void Builder::putI64(int64_t value,
|
||||
|
||||
void Builder::putF64(double number, uint8_t precision) const
|
||||
{
|
||||
precision = std::min(precision, static_cast<uint8_t>(std::numeric_limits<double>::digits10));
|
||||
char buffer[512];
|
||||
auto conversion = std::to_chars(buffer, buffer + sizeof(buffer), number);
|
||||
auto converted = std::string(buffer, conversion.ptr - buffer);
|
||||
|
||||
std::stringstream stream;
|
||||
stream
|
||||
<< std::fixed << std::setprecision(precision)
|
||||
<< number
|
||||
<< std::defaultfloat << std::setprecision(6);
|
||||
std::string string = stream.str();
|
||||
m_builder << string;
|
||||
size_t dot = converted.find('.');
|
||||
size_t length = dot + precision + 1;
|
||||
|
||||
// There is no number behind the decimal point
|
||||
if (dot == std::string::npos) {
|
||||
if (precision > 0) {
|
||||
converted += '.' + std::string(precision, '0');
|
||||
}
|
||||
m_builder << converted;
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are less numbers behind the decimal point than the precision
|
||||
if (converted.length() < length) {
|
||||
converted += std::string(length - converted.length(), '0');
|
||||
m_builder << converted;
|
||||
return;
|
||||
}
|
||||
|
||||
// Only round if there are more numbers behind the decimal point than the precision,
|
||||
// or the number that comes after the maximum precision is higher than 4
|
||||
if (converted.length() > length && converted[length] > '4') {
|
||||
for (size_t i = length - 1; i >= 0 && i < converted.length(); --i) {
|
||||
if (converted[i] == '.') {
|
||||
continue;
|
||||
}
|
||||
if (converted[i] < '9') { // Overflow stops here
|
||||
converted[i]++;
|
||||
break;
|
||||
}
|
||||
converted[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// Cut off the characters after the requested precision
|
||||
if (converted.length() > length) {
|
||||
size_t last_included_number = converted.find_last_not_of("0", length - 1);
|
||||
// If precision is zero, also cut off the '.', otherwise include the '0' after the '.'
|
||||
size_t last_character_is_dot_offset = (converted[last_included_number] == '.') ? (precision > 0 ? 1 : -1) : 0;
|
||||
converted = converted.substr(0, last_included_number + last_character_is_dot_offset + 1);
|
||||
}
|
||||
|
||||
m_builder << converted;
|
||||
}
|
||||
|
||||
void Builder::putString(std::string_view string, char fill, Align align, size_t width) const
|
||||
|
||||
@@ -81,6 +81,11 @@ void Formatter<const char*>::format(Builder& builder, const char* value) const
|
||||
value != nullptr ? std::string_view { value, strlen(value) } : "nullptr");
|
||||
}
|
||||
|
||||
void Formatter<const unsigned char*>::format(Builder& builder, const unsigned char* value) const
|
||||
{
|
||||
return Formatter<const char*>::format(builder, reinterpret_cast<const char*>(value));
|
||||
}
|
||||
|
||||
// Pointer
|
||||
|
||||
void Formatter<std::nullptr_t>::format(Builder& builder, std::nullptr_t) const
|
||||
|
||||
@@ -108,9 +108,11 @@ struct Formatter<T> {
|
||||
uint8_t base = 0;
|
||||
bool uppercase = false;
|
||||
switch (specifier.type) {
|
||||
case PresentationType::Binary:
|
||||
base = 2;
|
||||
break;
|
||||
case PresentationType::BinaryUppercase:
|
||||
uppercase = true;
|
||||
case PresentationType::Binary:
|
||||
base = 2;
|
||||
break;
|
||||
case PresentationType::Octal:
|
||||
@@ -120,9 +122,11 @@ struct Formatter<T> {
|
||||
case PresentationType::Decimal:
|
||||
base = 10;
|
||||
break;
|
||||
case PresentationType::Hex:
|
||||
base = 16;
|
||||
break;
|
||||
case PresentationType::HexUppercase:
|
||||
uppercase = true;
|
||||
case PresentationType::Hex:
|
||||
base = 16;
|
||||
break;
|
||||
default:
|
||||
@@ -195,6 +199,19 @@ template<size_t N>
|
||||
struct Formatter<char[N]> : Formatter<const char*> {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Formatter<const unsigned char*> : Formatter<const char*> {
|
||||
void format(Builder& builder, const unsigned char* value) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Formatter<unsigned char*> : Formatter<const unsigned char*> {
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct Formatter<unsigned char[N]> : Formatter<const unsigned char*> {
|
||||
};
|
||||
|
||||
// Pointer
|
||||
|
||||
template<typename T>
|
||||
@@ -314,6 +331,8 @@ struct Formatter<Specifier> : Formatter<std::nullptr_t> {
|
||||
|
||||
} // namespace ruc::format
|
||||
|
||||
using ruc::format::Formatter;
|
||||
|
||||
#if 0
|
||||
|
||||
TODO:
|
||||
|
||||
@@ -70,7 +70,8 @@ private:
|
||||
template<typename T>
|
||||
const LogOperatorStyle& operator<<(const LogOperatorStyle& logOperatorStyle, const T& value)
|
||||
{
|
||||
_format(const_cast<LogOperatorStyle&>(logOperatorStyle).builder(), value);
|
||||
Formatter<T> formatter;
|
||||
formatter.format(const_cast<LogOperatorStyle&>(logOperatorStyle).builder(), value);
|
||||
return logOperatorStyle;
|
||||
}
|
||||
|
||||
|
||||
@@ -411,6 +411,8 @@ constexpr void Parser::checkSpecifierType(const Specifier& specifier, ParameterT
|
||||
case ParameterType::Container:
|
||||
checkSpecifierContainerType(specifier);
|
||||
break;
|
||||
case ParameterType::UserDefined:
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
String,
|
||||
Pointer,
|
||||
Container,
|
||||
UserDefined,
|
||||
};
|
||||
|
||||
Parser(std::string_view format, size_t parameterCount);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstdio> // FILE, fputs, stdout, stderr
|
||||
#include <cstdio> // FILE, fflush, fputs, stdout, stderr
|
||||
#include <iomanip> // setprecision
|
||||
#include <ios> // defaultfloat, fixed
|
||||
#include <sstream> // stringstream
|
||||
@@ -22,6 +22,7 @@ void variadicPrint(FILE* file, std::string_view format, TypeErasedParameters& pa
|
||||
|
||||
std::string string = stream.str();
|
||||
fputs(string.c_str(), file);
|
||||
fflush(file);
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // transform
|
||||
#include <cstddef> // nullptr_t
|
||||
#include <cstddef> // nullptr_t, size_t
|
||||
#include <cstdint> // int32_t, int64_t, uint32_t
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "ruc/json/array.h"
|
||||
#include "ruc/json/object.h"
|
||||
#include "ruc/meta/assert.h"
|
||||
#include "ruc/meta/concepts.h"
|
||||
#include "ruc/meta/odr.h"
|
||||
|
||||
namespace ruc::json {
|
||||
@@ -44,15 +46,15 @@ void fromJson(const Json& json, bool& boolean)
|
||||
boolean = json.asBool();
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
void fromJson(const Json& json, int& number)
|
||||
template<typename Json, Integral T>
|
||||
void fromJson(const Json& json, T& number)
|
||||
{
|
||||
VERIFY(json.type() == Json::Type::Number);
|
||||
number = (int)json.asDouble();
|
||||
number = static_cast<T>(json.asDouble());
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
void fromJson(const Json& json, double& number)
|
||||
template<typename Json, FloatingPoint T>
|
||||
void fromJson(const Json& json, T& number)
|
||||
{
|
||||
VERIFY(json.type() == Json::Type::Number);
|
||||
number = json.asDouble();
|
||||
|
||||
@@ -266,7 +266,7 @@ Value Parser::consumeString()
|
||||
break;
|
||||
default:
|
||||
char buffer[7];
|
||||
sprintf(buffer, "\\u%0.4X", character);
|
||||
sprintf(buffer, "\\u%.4X", character);
|
||||
return std::string(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert> // assert
|
||||
#include <cstddef> // nullptr_t
|
||||
#include <cstddef> // nullptr_t, size_t
|
||||
#include <cstdint> // int32_t, int64_t, uint32_t
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "ruc/json/array.h"
|
||||
#include "ruc/json/object.h"
|
||||
#include "ruc/meta/concepts.h"
|
||||
#include "ruc/meta/odr.h"
|
||||
|
||||
namespace ruc::json {
|
||||
@@ -30,20 +31,20 @@ struct jsonConstructor {
|
||||
json.m_value.boolean = boolean;
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
static void construct(Json& json, int number)
|
||||
template<typename Json, Integral T>
|
||||
static void construct(Json& json, T number)
|
||||
{
|
||||
json.destroy();
|
||||
json.m_type = Json::Type::Number;
|
||||
json.m_value.number = (double)number;
|
||||
json.m_value.number = static_cast<double>(number);
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
static void construct(Json& json, double number)
|
||||
template<typename Json, FloatingPoint T>
|
||||
static void construct(Json& json, T number)
|
||||
{
|
||||
json.destroy();
|
||||
json.m_type = Json::Type::Number;
|
||||
json.m_value.number = number;
|
||||
json.m_value.number = static_cast<double>(number);
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
|
||||
@@ -71,8 +71,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.find("ruc::format::") != std::string_view::npos
|
||||
&& functionString.find("ruc::GenericLexer::") != std::string_view::npos) {
|
||||
if (functionString.find("ruc::format::") == std::string_view::npos
|
||||
&& functionString.find("ruc::GenericLexer::") == std::string_view::npos) {
|
||||
std::string message;
|
||||
formatTo(message, parameters...);
|
||||
fprintf(stderr, "%s", message.c_str());
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define BIT(x) (1 << x)
|
||||
|
||||
#define CONCAT_IMPL(a, b) a##b
|
||||
#define CONCAT(a, b) CONCAT_IMPL(a, b)
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool is(U& input)
|
||||
{
|
||||
if constexpr (requires { input.template fastIs<T>(); }) {
|
||||
return input.template fastIs<T>();
|
||||
}
|
||||
|
||||
return typeid(input) == typeid(T);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool is(U* input)
|
||||
{
|
||||
return input && is<T>(*input);
|
||||
}
|
||||
|
||||
// References:
|
||||
// - serenity/AK/TypeCasts.h
|
||||
// - serenity/Userland/Libraries/LibJS/AST.h
|
||||
+14
-4
@@ -6,7 +6,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib> // free, malloc
|
||||
|
||||
#include "meta/assert.h"
|
||||
|
||||
namespace ruc {
|
||||
|
||||
@@ -16,7 +18,10 @@ public:
|
||||
static inline T& the()
|
||||
{
|
||||
if (s_instance == nullptr) {
|
||||
s_instance = new T { s {} };
|
||||
// Allocate memory for this instance
|
||||
s_instance = static_cast<T*>(malloc(sizeof(T)));
|
||||
// Call the constructor using "placement new"
|
||||
new (s_instance) T { s {} };
|
||||
}
|
||||
|
||||
return *s_instance;
|
||||
@@ -24,10 +29,15 @@ public:
|
||||
|
||||
static inline void destroy()
|
||||
{
|
||||
if (s_instance) {
|
||||
delete s_instance;
|
||||
if (!s_instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the destructor
|
||||
s_instance->~T();
|
||||
// Deallocate memory
|
||||
free(static_cast<void*>(s_instance));
|
||||
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm> // std::sort, std::unique
|
||||
#include <cerrno> // errno, EAGAIN, EINTR
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdio> // perror, ssize_t
|
||||
|
||||
@@ -55,7 +55,7 @@ TEST_CASE(FormatIntegral)
|
||||
result = format("{}", u32);
|
||||
EXPECT_EQ(result, "4294967295");
|
||||
|
||||
size_t u64 = 18446744073709551615; // long unsigned int
|
||||
size_t u64 = 18446744073709551615u; // long unsigned int
|
||||
result = format("{}", u64);
|
||||
EXPECT_EQ(result, "18446744073709551615");
|
||||
}
|
||||
@@ -109,6 +109,14 @@ TEST_CASE(FormatString)
|
||||
result = format("{}", cString);
|
||||
EXPECT_EQ(result, "C string");
|
||||
|
||||
const unsigned char unsignedCharArray[] = "µnsïgned çhãr";
|
||||
result = format("{}", unsignedCharArray);
|
||||
EXPECT_EQ(result, "µnsïgned çhãr");
|
||||
|
||||
const unsigned char* unsignedCharPointer = &unsignedCharArray[0];
|
||||
result = format("{}", unsignedCharPointer);
|
||||
EXPECT_EQ(result, "µnsïgned çhãr");
|
||||
|
||||
std::string string = "string";
|
||||
result = format("{}", string);
|
||||
EXPECT_EQ(result, "string");
|
||||
@@ -513,5 +521,5 @@ TEST_CASE(FormatContainers)
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// lsp-in-cpp-project-cache: nil
|
||||
// lsp-in-cpp-project-cache: (nil)
|
||||
// End:
|
||||
|
||||
Reference in New Issue
Block a user