From d18a28a9659092952aef70a30a47726e7c16d31a Mon Sep 17 00:00:00 2001 From: rtk0c Date: Tue, 24 May 2022 21:47:55 -0700 Subject: Changeset: 38 Branch comment: [] Add infrastructure for codegen --- CMakeLists.txt | 50 ++++++++++++++++- buildtools/codegen/README.md | 5 ++ buildtools/codegen/main.cpp | 102 ++++++++++++++++++++++++++++++++++ source-common/Macros.hpp | 29 ++++++++++ source-common/MacrosCodegen.hpp | 4 ++ source-common/RapidJsonHelper.hpp | 110 +++++++++++++++++++++++++++++++++++++ source-common/ScopeGuard.hpp | 60 ++++++++++++++++++++ source-common/StbImplementations.c | 14 +++++ source-common/Utils.cpp | 87 +++++++++++++++++++++++++++++ source-common/Utils.hpp | 61 ++++++++++++++++++++ source-common/YCombinator.hpp | 14 +++++ source/CMakeLists.txt | 2 - source/Macros.hpp | 29 ---------- source/RapidJsonHelper.hpp | 110 ------------------------------------- source/ScopeGuard.hpp | 60 -------------------- source/Utils.cpp | 87 ----------------------------- source/Utils.hpp | 61 -------------------- source/YCombinator.hpp | 14 ----- source/stb_implementation.c | 11 ---- 19 files changed, 535 insertions(+), 375 deletions(-) create mode 100644 buildtools/codegen/README.md create mode 100644 buildtools/codegen/main.cpp create mode 100644 source-common/Macros.hpp create mode 100644 source-common/MacrosCodegen.hpp create mode 100644 source-common/RapidJsonHelper.hpp create mode 100644 source-common/ScopeGuard.hpp create mode 100644 source-common/StbImplementations.c create mode 100644 source-common/Utils.cpp create mode 100644 source-common/Utils.hpp create mode 100644 source-common/YCombinator.hpp delete mode 100644 source/Macros.hpp delete mode 100644 source/RapidJsonHelper.hpp delete mode 100644 source/ScopeGuard.hpp delete mode 100644 source/Utils.cpp delete mode 100644 source/Utils.hpp delete mode 100644 source/YCombinator.hpp delete mode 100644 source/stb_implementation.c diff --git a/CMakeLists.txt b/CMakeLists.txt index ad2bf16..3d349c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,50 @@ add_subdirectory(3rdparty/imgui) add_subdirectory(3rdparty/imguicolortextedit) add_subdirectory(3rdparty/tracy) +# ============================================================================== + +file(GLOB_RECURSE commonthings_SOURCES source-common/*.c source-common/*.cpp) +add_library(commonthings OBJECT ${commonthings_SOURCES}) + +set_target_properties(commonthings PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + + # Many files include platform headers, we don't want to leak them - it's just simpler to disable unity build for everything + UNITY_BUILD OFF +) + +target_include_directories(commonthings PUBLIC source-common) +target_link_libraries(commonthings PUBLIC + # External dependencies + ${CONAN_LIBS} + glm::glm +) + +# ============================================================================== + +# NOTE: delibrately not recursive, see README.md in the folder for details +file(GLOB meta_codegen_SOURCES buildtools/codegen/*.c buildtools/codegen/*.cpp) +add_executable(meta_codegen ${meta_codegen_SOURCES}) + +set_target_properties(meta_codegen PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + UNITY_BUILD OFF +) + +target_link_libraries(meta_codegen PRIVATE + # External dependencies + ${CONAN_LIBS} + + # Project internal components + commonthings +) + +# ============================================================================== + # add_executable requires at least one source file add_executable(${PROJECT_NAME} dummy.c) add_subdirectory(source) @@ -20,8 +64,8 @@ set_target_properties(${PROJECT_NAME} PROPERTIES UNITY_BUILD_UNIQUE_ID "${PROJECT_NAME}_UNITY_ID" ) -target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF ) @@ -37,6 +81,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE ) target_link_libraries(${PROJECT_NAME} PRIVATE + # External dependencies ${CONAN_LIBS} OpenGL::GL glfw @@ -44,6 +89,9 @@ target_link_libraries(${PROJECT_NAME} PRIVATE imgui ImGuiColorTextEdit tracy + + # Project internal components + commonthings ) option(BRUSSEL_ENABLE_PROFILING "Whether profiling support is enabled or not." OFF) diff --git a/buildtools/codegen/README.md b/buildtools/codegen/README.md new file mode 100644 index 0000000..7164132 --- /dev/null +++ b/buildtools/codegen/README.md @@ -0,0 +1,5 @@ +# Code Generator +The main code generator. + +## Folder structure +The main program's source files are all located in this folder directly. Text tempaltes are located in `templates/` and none of the files are compiled (even if they end with .c or .cpp). diff --git a/buildtools/codegen/main.cpp b/buildtools/codegen/main.cpp new file mode 100644 index 0000000..4a1d486 --- /dev/null +++ b/buildtools/codegen/main.cpp @@ -0,0 +1,102 @@ +#include "ScopeGuard.hpp" +#include "Utils.hpp" + +#include +#include +#include +#include + +using namespace std::literals; +namespace fs = std::filesystem; + +enum InputOpcode { + IOP_ProcessSingleFile, + IOP_ProcessRecursively, + IOP_COUNT, +}; + +InputOpcode ParseInputOpcode(std::string_view text) { + if (text == "single"sv) { + return IOP_ProcessSingleFile; + } else if (text == "rec"sv) { + return IOP_ProcessRecursively; + } + return IOP_COUNT; +} + +void HandleInputFile(std::string_view source) { + stb_lexer lexer; + char stringStorage[65536]; + const char* srcBegin = source.data(); + const char* srcEnd = srcBegin + source.length(); + stb_c_lexer_init(&lexer, srcBegin, srcEnd, stringStorage, sizeof(stringStorage)); + + // TODO +} + +std::string ReadFileAtOnce(const fs::path& path) { + auto file = Utils::OpenCstdioFile(path, Utils::Read); + if (!file) throw std::runtime_error("Failed to open source file."); + DEFER { fclose(file); }; + + fseek(file, 0, SEEK_END); + auto fileSize = ftell(file); + rewind(file); + + std::string result(fileSize, '\0'); + fread(result.data(), fileSize, 1, file); + + return result; +} + +void HandleArgument(InputOpcode opcode, std::string_view operand) { + switch (opcode) { + case IOP_ProcessSingleFile: { + fs::path filePath(operand); + auto source = ReadFileAtOnce(filePath); + HandleInputFile(source); + } break; + + case IOP_ProcessRecursively: { + fs::path startPath(operand); + for (auto& item : fs::directory_iterator(startPath)) { + if (!item.is_regular_file()) { + continue; + } + + auto& path = item.path(); + auto filename = path.filename().string(); + if (filename != ".c" || + filename != ".cpp") + { + continue; + } + + auto source = ReadFileAtOnce(path); + HandleInputFile(source); + } + } break; + + case IOP_COUNT: break; + } +} + +int main(int argc, char* argv[]) { + // TODO better arg parser + // option 1: use cxxopts and positional arguments + // option 1: take one argument only, being a json objecet + + // If no cli is provided (argv[0]), this loop will do nothing + // Otherwise, start with the 2nd element which is the 1st argument + for (int i = 1; i < argc; ++i) { + std::string_view arg(argv[i]); + auto separatorLoc = arg.find(':'); + if (separatorLoc != std::string_view::npos) { + auto opcode = ParseInputOpcode(arg.substr(0, separatorLoc)); + auto operand = arg.substr(separatorLoc); + HandleArgument(opcode, operand); + } + } + + return 0; +} diff --git a/source-common/Macros.hpp b/source-common/Macros.hpp new file mode 100644 index 0000000..b5d05fa --- /dev/null +++ b/source-common/Macros.hpp @@ -0,0 +1,29 @@ +#pragma once + +#define STRINGIFY_IMPL(text) #text +#define STRINGIFY(text) STRINGIFY_IMPL(text) + +#define CONCAT_IMPL(a, b) a##b +#define CONCAT(a, b) CONCAT_IMPL(a, b) +#define CONCAT_3(a, b, c) CONCAT(a, CONCAT(b, c)) +#define CONCAT_4(a, b, c, d) CONCAT(CONCAT(a, b), CONCAT(c, d)) + +#define UNIQUE_NAME(prefix) CONCAT(prefix, __COUNTER__) +#define UNIQUE_NAME_LINE(prefix) CONCAT(prefix, __LINE__) +#define DISCARD UNIQUE_NAME(_discard) + +#define UNUSED(x) (void)x; + +#if defined(_MSC_VER) +# define UNREACHABLE __assume(0) +#elif defined(__GNUC__) || defined(__clang__) +# define UNREACHABLE __builtin_unreachable() +#else +# define UNREACHABLE +#endif + +#if _WIN32 +# define PLATFORM_PATH_STR "%ls" +#else +# define PLATFORM_PATH_STR "%s" +#endif diff --git a/source-common/MacrosCodegen.hpp b/source-common/MacrosCodegen.hpp new file mode 100644 index 0000000..6c93d09 --- /dev/null +++ b/source-common/MacrosCodegen.hpp @@ -0,0 +1,4 @@ +#pragma once + +#define BRUSSEL_METACLASS +#define BRUSSEL_METAENUM(name) diff --git a/source-common/RapidJsonHelper.hpp b/source-common/RapidJsonHelper.hpp new file mode 100644 index 0000000..75cd93a --- /dev/null +++ b/source-common/RapidJsonHelper.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include + +#define BRUSSEL_JSON_GET(object, name, type, out, failAction) \ + { \ + auto it = (object).FindMember(name); \ + if (it == (object).MemberEnd()) failAction; \ + auto& value = it->value; \ + if (!value.Is()) failAction; \ + (out) = value.Get(); \ + } + +#define BRUSSEL_JSON_GET_DEFAULT(object, name, type, out, theDefault) \ + do { \ + auto it = (object).FindMember(name); \ + if (it == (object).MemberEnd()) { \ + (out) = theDefault; \ + break; \ + } \ + auto& value = it->value; \ + if (!value.Is()) { \ + (out) = theDefault; \ + break; \ + } \ + (out) = value.Get(); \ + } while (0); + +namespace rapidjson { + +inline const Value* GetProperty(const Value& value, std::string_view name) { + for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) { + if (it->name.GetStringLength() != name.size()) continue; + if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue; + + return &it->value; + } + return nullptr; +} + +inline const Value* GetProperty(const Value& value, Type type, std::string_view name) { + for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) { + if (it->name.GetStringLength() != name.size()) continue; + if (it->value.GetType() != type) continue; + if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue; + + return &it->value; + } + return nullptr; +} + +inline std::string_view AsStringView(const Value& value) { + return std::string_view(value.GetString(), value.GetStringLength()); +} + +inline std::string_view AsStringView(const GenericStringRef& strRef) { + return std::string_view(strRef.s, strRef.length); +} + +inline std::string AsString(const Value& value) { + return std::string(value.GetString(), value.GetStringLength()); +} + +inline std::string AsString(const GenericStringRef& strRef) { + return std::string(strRef.s, strRef.length); +} + +// RapidJson itself already provides std::string and const char* overloads +inline GenericStringRef StringRef(std::string_view str) { + return GenericStringRef( + str.data() ? str.data() : "", + str.size()); +} + +template +rapidjson::Value WriteVectorPrimitives(rapidjson::Document& root, TIter begin, TSentienl end) { + using TElement = typename TIter::value_type; + + rapidjson::Value list; + while (begin != end) { + if constexpr (std::is_same_v) { + auto& elm = *begin; + list.PushBack(rapidjson::Value(elm.c_str(), elm.size()), root.GetAllocator()); + } else { + list.PushBack(*begin, root.GetAllocator()); + } + ++begin; + } + return list; +} + +template +bool ReadVectorPrimitives(const rapidjson::Value& value, TContainer& list) { + using TElement = typename TContainer::value_type; + + if (!value.IsArray()) return false; + + list.reserve(value.Size()); + for (auto& elm : value.GetArray()) { + if (!elm.Is()) return {}; + list.push_back(elm.Get()); + } + + return true; +} + +} // namespace rapidjson diff --git a/source-common/ScopeGuard.hpp b/source-common/ScopeGuard.hpp new file mode 100644 index 0000000..28f3385 --- /dev/null +++ b/source-common/ScopeGuard.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "Macros.hpp" + +#include + +template +class ScopeGuard { +private: + TCleanupFunc mFunc; + bool mDismissed = false; + +public: + /// Specifically left this implicit so that constructs like + /// \code + /// ScopeGuard sg = [&]() { res.Cleanup(); }; + /// \endcode + /// would work. It is highly discourage and unlikely that one would want to use ScopeGuard as a function + /// parameter, so the normal argument that implicit conversion are harmful doesn't really apply here. + // Deliberately not explicit to allow usages like: ScopeGuard var = lambda; + ScopeGuard(TCleanupFunc&& function) noexcept + : mFunc{ std::move(function) } { + } + + ~ScopeGuard() noexcept { + if (!mDismissed) { + mFunc(); + } + } + + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; + + ScopeGuard(ScopeGuard&& that) noexcept + : mFunc{ std::move(that.mFunc) } { + that.Cancel(); + } + + ScopeGuard& operator=(ScopeGuard&& that) noexcept { + if (!mDismissed) { + mFunc(); + } + this->mFunc = std::move(that.mFunc); + this->cancelled = std::exchange(that.cancelled, true); + } + + void Dismiss() noexcept { + mDismissed = true; + } +}; + +template +auto GuardDeletion(T* ptr) { + return ScopeGuard([ptr]() { + delete ptr; + }); +} + +#define SCOPE_GUARD(name) ScopeGuard name = [&]() +#define DEFER ScopeGuard UNIQUE_NAME(scopeGuard) = [&]() diff --git a/source-common/StbImplementations.c b/source-common/StbImplementations.c new file mode 100644 index 0000000..73bbc2a --- /dev/null +++ b/source-common/StbImplementations.c @@ -0,0 +1,14 @@ +#define STB_RECT_PACK_IMPLEMENTATION +#include + +#define STB_TRUETYPE_IMPLEMENTATION +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +#define STB_SPRINTF_IMPLEMENTATION +#include + +#define STB_C_LEXER_IMPLEMENTATION +#include diff --git a/source-common/Utils.cpp b/source-common/Utils.cpp new file mode 100644 index 0000000..53b3863 --- /dev/null +++ b/source-common/Utils.cpp @@ -0,0 +1,87 @@ +#include "Utils.hpp" + +#ifdef _WIN32 +# include +#endif + +#ifdef _WIN32 +# define BRUSSEL_MODE_STRING(string) L##string +#else +# define BRUSSEL_MODE_STRING(string) string +#endif + +#if _WIN32 +using FopenModeString = const wchar_t*; +#else +using FopenModeString = const char*; +#endif + +static FopenModeString GetModeString(Utils::IoMode mode, bool binary) { + using namespace Utils; + if (binary) { + switch (mode) { + case Read: return BRUSSEL_MODE_STRING("rb"); + case WriteTruncate: return BRUSSEL_MODE_STRING("wb"); + case WriteAppend: return BRUSSEL_MODE_STRING("ab"); + } + } else { + switch (mode) { + case Read: return BRUSSEL_MODE_STRING("r"); + case WriteTruncate: return BRUSSEL_MODE_STRING("w"); + case WriteAppend: return BRUSSEL_MODE_STRING("a"); + } + } + return nullptr; +} + +FILE* Utils::OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary) { +#ifdef _WIN32 + // std::filesystem::path::c_str() returns `const wchar_t*` under Windows, because NT uses UTF-16 natively + // NOTE: _wfopen() only affects the type of path parameter, otherwise the file stream created is identical to the one by fopen() + return _wfopen(path.c_str(), ::GetModeString(mode, binary)); +#else + return fopen(path.c_str(), ::GetModeString(mode, binary)); +#endif +} + +FILE* Utils::OpenCstdioFile(const char* path, IoMode mode, bool binary) { +#ifdef _WIN32 + // On Windows, fopen() accepts ANSI codepage encoded path, convert our UTF-8 string to UTF-16 to ensure that no matter what the locale is, the path continues to work + WCHAR platformPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, platformPath, MAX_PATH) == 0) { + return nullptr; + } + return _wfopen(platformPath, ::GetModeString(mode, binary)); +#else + return fopen(path, ::GetModeString(mode, binary)); +#endif +} + +bool Utils::InRangeInclusive(int n, int lower, int upper) { + if (lower > upper) { + std::swap(lower, upper); + } + return n >= lower && n <= upper; +} + +bool Utils::LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate) { + bool verticalLine = p1.x == p2.x && InRangeInclusive(candidate.x, p1.x, p2.x); + bool horizontalLine = p1.y == p2.y && InRangeInclusive(candidate.y, p1.y, p2.y); + return verticalLine && horizontalLine; +} + +bool Utils::IsColinear(glm::ivec2 p1, glm::ivec2 p2) { + return p1.x == p2.x || p1.y == p2.y; +} + +std::string Utils::MakeRandomNumberedName(const char* tag) { + int n = std::rand(); +#define RNG_NAME_PATTERN "Unnamed %s #%d", tag, n + // NOTE: does not include null-terminator + int size = snprintf(nullptr, 0, RNG_NAME_PATTERN); + std::string result; + result.resize(size); // std::string::resize handles storage for null-terminator alreaedy + snprintf(result.data(), size, RNG_NAME_PATTERN); +#undef RNG_NAME_PATTERN + return result; +} diff --git a/source-common/Utils.hpp b/source-common/Utils.hpp new file mode 100644 index 0000000..9f28aad --- /dev/null +++ b/source-common/Utils.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Utils { + +enum IoMode { + Read, + WriteTruncate, + WriteAppend, +}; + +FILE* OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary = false); +FILE* OpenCstdioFile(const char* path, IoMode mode, bool binary = false); + +constexpr float Abs(float v) noexcept { + return v < 0.0f ? -v : v; +} + +bool InRangeInclusive(int n, int lower, int upper); +bool LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate); + +bool IsColinear(glm::ivec2 p1, glm::ivec2 p2); + +template +void HashCombine(std::size_t& seed, const T& v) { + seed ^= std::hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +std::string MakeRandomNumberedName(const char* tag); + +} // namespace Utils + +struct StringHash { + using is_transparent = void; + + std::size_t operator()(const std::string& key) const { return robin_hood::hash_bytes(key.c_str(), key.size()); } + std::size_t operator()(std::string_view key) const { return robin_hood::hash_bytes(key.data(), key.size()); } + std::size_t operator()(const char* key) const { return robin_hood::hash_bytes(key, std::strlen(key)); } +}; + +struct StringEqual { + using is_transparent = int; + + bool operator()(std::string_view lhs, const std::string& rhs) const { + const std::string_view view = rhs; + return lhs == view; + } + + bool operator()(const char* lhs, const std::string& rhs) const { + return std::strcmp(lhs, rhs.c_str()) == 0; + } + + bool operator()(const std::string& lhs, const std::string& rhs) const { + return lhs == rhs; + } +}; diff --git a/source-common/YCombinator.hpp b/source-common/YCombinator.hpp new file mode 100644 index 0000000..b1d2350 --- /dev/null +++ b/source-common/YCombinator.hpp @@ -0,0 +1,14 @@ +#pragma once + +template +struct YCombinator { + // NOTE: implicit constructor allows initializing this + Func func; + + template + decltype(auto) operator()(Ts&&... args) const { + // NOTE: static_cast(args)... is equivalent to std::forward(args)... + // written this way so that we don't have to include , as well as reducing template instanciations to help compile time + return func(*this, static_cast(args)...); + } +}; diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 6ca2cd5..b5de147 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -31,9 +31,7 @@ PRIVATE ) set(ProjectBrussel_SINGLE_UNIT_SRC - stb_implementation.c main.cpp - Utils.cpp # May include platform headers ) target_sources(${PROJECT_NAME} PRIVATE ${ProjectBrussel_SINGLE_UNIT_SRC}) set_source_files_properties(${ProjectBrussel_SINGLE_UNIT_SRC} diff --git a/source/Macros.hpp b/source/Macros.hpp deleted file mode 100644 index b5d05fa..0000000 --- a/source/Macros.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#define STRINGIFY_IMPL(text) #text -#define STRINGIFY(text) STRINGIFY_IMPL(text) - -#define CONCAT_IMPL(a, b) a##b -#define CONCAT(a, b) CONCAT_IMPL(a, b) -#define CONCAT_3(a, b, c) CONCAT(a, CONCAT(b, c)) -#define CONCAT_4(a, b, c, d) CONCAT(CONCAT(a, b), CONCAT(c, d)) - -#define UNIQUE_NAME(prefix) CONCAT(prefix, __COUNTER__) -#define UNIQUE_NAME_LINE(prefix) CONCAT(prefix, __LINE__) -#define DISCARD UNIQUE_NAME(_discard) - -#define UNUSED(x) (void)x; - -#if defined(_MSC_VER) -# define UNREACHABLE __assume(0) -#elif defined(__GNUC__) || defined(__clang__) -# define UNREACHABLE __builtin_unreachable() -#else -# define UNREACHABLE -#endif - -#if _WIN32 -# define PLATFORM_PATH_STR "%ls" -#else -# define PLATFORM_PATH_STR "%s" -#endif diff --git a/source/RapidJsonHelper.hpp b/source/RapidJsonHelper.hpp deleted file mode 100644 index 75cd93a..0000000 --- a/source/RapidJsonHelper.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#define BRUSSEL_JSON_GET(object, name, type, out, failAction) \ - { \ - auto it = (object).FindMember(name); \ - if (it == (object).MemberEnd()) failAction; \ - auto& value = it->value; \ - if (!value.Is()) failAction; \ - (out) = value.Get(); \ - } - -#define BRUSSEL_JSON_GET_DEFAULT(object, name, type, out, theDefault) \ - do { \ - auto it = (object).FindMember(name); \ - if (it == (object).MemberEnd()) { \ - (out) = theDefault; \ - break; \ - } \ - auto& value = it->value; \ - if (!value.Is()) { \ - (out) = theDefault; \ - break; \ - } \ - (out) = value.Get(); \ - } while (0); - -namespace rapidjson { - -inline const Value* GetProperty(const Value& value, std::string_view name) { - for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) { - if (it->name.GetStringLength() != name.size()) continue; - if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue; - - return &it->value; - } - return nullptr; -} - -inline const Value* GetProperty(const Value& value, Type type, std::string_view name) { - for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) { - if (it->name.GetStringLength() != name.size()) continue; - if (it->value.GetType() != type) continue; - if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue; - - return &it->value; - } - return nullptr; -} - -inline std::string_view AsStringView(const Value& value) { - return std::string_view(value.GetString(), value.GetStringLength()); -} - -inline std::string_view AsStringView(const GenericStringRef& strRef) { - return std::string_view(strRef.s, strRef.length); -} - -inline std::string AsString(const Value& value) { - return std::string(value.GetString(), value.GetStringLength()); -} - -inline std::string AsString(const GenericStringRef& strRef) { - return std::string(strRef.s, strRef.length); -} - -// RapidJson itself already provides std::string and const char* overloads -inline GenericStringRef StringRef(std::string_view str) { - return GenericStringRef( - str.data() ? str.data() : "", - str.size()); -} - -template -rapidjson::Value WriteVectorPrimitives(rapidjson::Document& root, TIter begin, TSentienl end) { - using TElement = typename TIter::value_type; - - rapidjson::Value list; - while (begin != end) { - if constexpr (std::is_same_v) { - auto& elm = *begin; - list.PushBack(rapidjson::Value(elm.c_str(), elm.size()), root.GetAllocator()); - } else { - list.PushBack(*begin, root.GetAllocator()); - } - ++begin; - } - return list; -} - -template -bool ReadVectorPrimitives(const rapidjson::Value& value, TContainer& list) { - using TElement = typename TContainer::value_type; - - if (!value.IsArray()) return false; - - list.reserve(value.Size()); - for (auto& elm : value.GetArray()) { - if (!elm.Is()) return {}; - list.push_back(elm.Get()); - } - - return true; -} - -} // namespace rapidjson diff --git a/source/ScopeGuard.hpp b/source/ScopeGuard.hpp deleted file mode 100644 index 28f3385..0000000 --- a/source/ScopeGuard.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "Macros.hpp" - -#include - -template -class ScopeGuard { -private: - TCleanupFunc mFunc; - bool mDismissed = false; - -public: - /// Specifically left this implicit so that constructs like - /// \code - /// ScopeGuard sg = [&]() { res.Cleanup(); }; - /// \endcode - /// would work. It is highly discourage and unlikely that one would want to use ScopeGuard as a function - /// parameter, so the normal argument that implicit conversion are harmful doesn't really apply here. - // Deliberately not explicit to allow usages like: ScopeGuard var = lambda; - ScopeGuard(TCleanupFunc&& function) noexcept - : mFunc{ std::move(function) } { - } - - ~ScopeGuard() noexcept { - if (!mDismissed) { - mFunc(); - } - } - - ScopeGuard(const ScopeGuard&) = delete; - ScopeGuard& operator=(const ScopeGuard&) = delete; - - ScopeGuard(ScopeGuard&& that) noexcept - : mFunc{ std::move(that.mFunc) } { - that.Cancel(); - } - - ScopeGuard& operator=(ScopeGuard&& that) noexcept { - if (!mDismissed) { - mFunc(); - } - this->mFunc = std::move(that.mFunc); - this->cancelled = std::exchange(that.cancelled, true); - } - - void Dismiss() noexcept { - mDismissed = true; - } -}; - -template -auto GuardDeletion(T* ptr) { - return ScopeGuard([ptr]() { - delete ptr; - }); -} - -#define SCOPE_GUARD(name) ScopeGuard name = [&]() -#define DEFER ScopeGuard UNIQUE_NAME(scopeGuard) = [&]() diff --git a/source/Utils.cpp b/source/Utils.cpp deleted file mode 100644 index 53b3863..0000000 --- a/source/Utils.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "Utils.hpp" - -#ifdef _WIN32 -# include -#endif - -#ifdef _WIN32 -# define BRUSSEL_MODE_STRING(string) L##string -#else -# define BRUSSEL_MODE_STRING(string) string -#endif - -#if _WIN32 -using FopenModeString = const wchar_t*; -#else -using FopenModeString = const char*; -#endif - -static FopenModeString GetModeString(Utils::IoMode mode, bool binary) { - using namespace Utils; - if (binary) { - switch (mode) { - case Read: return BRUSSEL_MODE_STRING("rb"); - case WriteTruncate: return BRUSSEL_MODE_STRING("wb"); - case WriteAppend: return BRUSSEL_MODE_STRING("ab"); - } - } else { - switch (mode) { - case Read: return BRUSSEL_MODE_STRING("r"); - case WriteTruncate: return BRUSSEL_MODE_STRING("w"); - case WriteAppend: return BRUSSEL_MODE_STRING("a"); - } - } - return nullptr; -} - -FILE* Utils::OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary) { -#ifdef _WIN32 - // std::filesystem::path::c_str() returns `const wchar_t*` under Windows, because NT uses UTF-16 natively - // NOTE: _wfopen() only affects the type of path parameter, otherwise the file stream created is identical to the one by fopen() - return _wfopen(path.c_str(), ::GetModeString(mode, binary)); -#else - return fopen(path.c_str(), ::GetModeString(mode, binary)); -#endif -} - -FILE* Utils::OpenCstdioFile(const char* path, IoMode mode, bool binary) { -#ifdef _WIN32 - // On Windows, fopen() accepts ANSI codepage encoded path, convert our UTF-8 string to UTF-16 to ensure that no matter what the locale is, the path continues to work - WCHAR platformPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, platformPath, MAX_PATH) == 0) { - return nullptr; - } - return _wfopen(platformPath, ::GetModeString(mode, binary)); -#else - return fopen(path, ::GetModeString(mode, binary)); -#endif -} - -bool Utils::InRangeInclusive(int n, int lower, int upper) { - if (lower > upper) { - std::swap(lower, upper); - } - return n >= lower && n <= upper; -} - -bool Utils::LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate) { - bool verticalLine = p1.x == p2.x && InRangeInclusive(candidate.x, p1.x, p2.x); - bool horizontalLine = p1.y == p2.y && InRangeInclusive(candidate.y, p1.y, p2.y); - return verticalLine && horizontalLine; -} - -bool Utils::IsColinear(glm::ivec2 p1, glm::ivec2 p2) { - return p1.x == p2.x || p1.y == p2.y; -} - -std::string Utils::MakeRandomNumberedName(const char* tag) { - int n = std::rand(); -#define RNG_NAME_PATTERN "Unnamed %s #%d", tag, n - // NOTE: does not include null-terminator - int size = snprintf(nullptr, 0, RNG_NAME_PATTERN); - std::string result; - result.resize(size); // std::string::resize handles storage for null-terminator alreaedy - snprintf(result.data(), size, RNG_NAME_PATTERN); -#undef RNG_NAME_PATTERN - return result; -} diff --git a/source/Utils.hpp b/source/Utils.hpp deleted file mode 100644 index 9f28aad..0000000 --- a/source/Utils.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace Utils { - -enum IoMode { - Read, - WriteTruncate, - WriteAppend, -}; - -FILE* OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary = false); -FILE* OpenCstdioFile(const char* path, IoMode mode, bool binary = false); - -constexpr float Abs(float v) noexcept { - return v < 0.0f ? -v : v; -} - -bool InRangeInclusive(int n, int lower, int upper); -bool LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate); - -bool IsColinear(glm::ivec2 p1, glm::ivec2 p2); - -template -void HashCombine(std::size_t& seed, const T& v) { - seed ^= std::hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); -} - -std::string MakeRandomNumberedName(const char* tag); - -} // namespace Utils - -struct StringHash { - using is_transparent = void; - - std::size_t operator()(const std::string& key) const { return robin_hood::hash_bytes(key.c_str(), key.size()); } - std::size_t operator()(std::string_view key) const { return robin_hood::hash_bytes(key.data(), key.size()); } - std::size_t operator()(const char* key) const { return robin_hood::hash_bytes(key, std::strlen(key)); } -}; - -struct StringEqual { - using is_transparent = int; - - bool operator()(std::string_view lhs, const std::string& rhs) const { - const std::string_view view = rhs; - return lhs == view; - } - - bool operator()(const char* lhs, const std::string& rhs) const { - return std::strcmp(lhs, rhs.c_str()) == 0; - } - - bool operator()(const std::string& lhs, const std::string& rhs) const { - return lhs == rhs; - } -}; diff --git a/source/YCombinator.hpp b/source/YCombinator.hpp deleted file mode 100644 index b1d2350..0000000 --- a/source/YCombinator.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -template -struct YCombinator { - // NOTE: implicit constructor allows initializing this - Func func; - - template - decltype(auto) operator()(Ts&&... args) const { - // NOTE: static_cast(args)... is equivalent to std::forward(args)... - // written this way so that we don't have to include , as well as reducing template instanciations to help compile time - return func(*this, static_cast(args)...); - } -}; diff --git a/source/stb_implementation.c b/source/stb_implementation.c deleted file mode 100644 index 078ca5d..0000000 --- a/source/stb_implementation.c +++ /dev/null @@ -1,11 +0,0 @@ -#define STB_RECT_PACK_IMPLEMENTATION -#include - -#define STB_TRUETYPE_IMPLEMENTATION -#include - -#define STB_IMAGE_IMPLEMENTATION -#include - -#define STB_SPRINTF_IMPLEMENTATION -#include -- cgit v1.2.3-70-g09d2