#pragma once #include #include #include #include "Platform.h" #include "types.h" namespace fmt { template static std::string format(const char*, const Args&...); } template struct fmt_unveil { static_assert(sizeof(T) > 0, "fmt_unveil<>: cannot pass forward-declared object"); using type = T; static inline u64 get(const T& arg) { return reinterpret_cast(&arg); } // Temporary value container (can possibly be created by other fmt_unveil<> specializations) struct u64_wrapper { T arg; // Allow implicit conversion operator u64() const { return reinterpret_cast(&arg); } }; // This overload resolution takes the precedence static inline u64_wrapper get(T&& arg) { return {std::move(arg)}; } }; template struct fmt_unveil::value && sizeof(T) <= 8 && alignof(T) <= 8>> { using type = T; static inline u64 get(T arg) { return static_cast(arg); } }; template struct fmt_unveil::value && sizeof(T) <= 8 && alignof(T) <= 8>> { using type = T; // Convert FP to f64 and reinterpret (TODO?) static inline u64 get(f64 arg) { return reinterpret_cast(arg); } }; template struct fmt_unveil::value>> { using type = T; static inline u64 get(T arg) { return static_cast>(arg); } }; template struct fmt_unveil { using type = const T*; static inline u64 get(const T* arg) { return reinterpret_cast(arg); } }; template struct fmt_unveil { using type = const T*; static inline u64 get(const T* arg) { return reinterpret_cast(arg); } }; template<> struct fmt_unveil { using type = bool; static inline u64 get(const b8& value) { return fmt_unveil::get(value); } }; // String type format provider, also type classifier (format() called if an argument is formatted as "%s") template struct fmt_class_string { // Formatting function (must be explicitly specialized) static void format(std::string& out, u64 arg); // Helper typedef (visible in format()) using type = T; // Helper function (converts arg to object reference) static SAFE_BUFFERS FORCE_INLINE const T& get_object(u64 arg) { return *reinterpret_cast(static_cast(arg)); } // Helper function (safely converts arg to enum value) static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, const char*(*get)(T value)) { const auto value = static_cast>(arg); // Check narrowing if (static_cast(value) == arg) { if (const char* str = get(static_cast(value))) { out += str; return; } } // Fallback to underlying type formatting fmt_class_string>::format(out, static_cast(value)); } // Helper function (bitset formatting) static SAFE_BUFFERS FORCE_INLINE void format_bitset(std::string& out, u64 arg, const char* prefix, const char* delim, const char* suffix, void(*fmt)(std::string&, u64)) { // Start from raw value fmt_class_string::format(out, arg); out += prefix; for (u64 i = 0; i < 64; i++) { const u64 mask = 1ull << i; if (arg & mask) { fmt(out, i); if (arg > mask) { out += delim; } } } out += suffix; } // Helper constant (may be used in format_enum as lambda return value) static constexpr const char* unknown = nullptr; }; template<> struct fmt_class_string { static void format(std::string& out, u64 arg); }; template struct fmt_class_string : fmt_class_string { // Classify all pointers as const void* }; template<> struct fmt_class_string { static void format(std::string& out, u64 arg); }; template<> struct fmt_class_string : fmt_class_string { // Classify char* as const char* }; struct fmt_type_info { decltype(&fmt_class_string::format) fmt_string; template static constexpr fmt_type_info make() { return fmt_type_info { &fmt_class_string::format, }; } template static inline const fmt_type_info* get() { // Constantly initialized null-terminated list of type-specific information static constexpr fmt_type_info result[sizeof...(Args) + 1] { make()... }; return result; } }; template using fmt_unveil_t = typename fmt_unveil::type; // Argument array type (each element generated via fmt_unveil<>) template using fmt_args_t = const u64(&&)[sizeof...(Args) + 1]; namespace fmt { // Internal formatting function void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept; // Formatting function template static SAFE_BUFFERS void append(std::string& out, const char* fmt, const Args&... args) { raw_append(out, fmt, fmt_type_info::get...>(), fmt_args_t{fmt_unveil::get(args)...}); } // Formatting function template static SAFE_BUFFERS std::string format(const char* fmt, const Args&... args) { std::string result; append(result, fmt, args...); return result; } // Internal helper function char* alloc_format(const char*, const fmt_type_info*, const u64*) noexcept; // Exception type with formatting constructor template class exception_t : public Base { using base = Base; public: template SAFE_BUFFERS exception_t(const char* fmt, const Args&... args) : base((fmt = alloc_format(fmt, fmt_type_info::get...>(), fmt_args_t{fmt_unveil::get(args)...}))) { std::free(const_cast(fmt)); } }; // Exception type derived from std::runtime_error with formatting constructor using exception = exception_t; }