#pragma once #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); } }; 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 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* }; namespace fmt { // Argument array type (each element generated via fmt_unveil<>) template using args_t = const u64(&&)[sizeof...(Args) + 1]; using supplementary_info = const struct arg_type_info; struct arg_type_info { decltype(&fmt_class_string::format) fmt_string; template static constexpr arg_type_info make() { return arg_type_info { &fmt_class_string::format, }; } template static inline const supplementary_info* get() { // Constantly initialized null-terminated list of type-specific information static constexpr arg_type_info result[sizeof...(Args) + 1] { make()... }; return result; } }; // Internal formatting function void raw_append(std::string& out, const char*, const supplementary_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, arg_type_info::get::type...>(), 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 supplementary_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, arg_type_info::get::type...>(), 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; }