2014-04-01 02:33:55 +02:00
|
|
|
#pragma once
|
2015-01-12 19:12:06 +01:00
|
|
|
|
2016-04-27 00:27:24 +02:00
|
|
|
#include <exception>
|
2016-05-13 16:01:48 +02:00
|
|
|
#include <string>
|
2016-08-07 15:59:46 +02:00
|
|
|
#include <memory>
|
2016-02-01 22:55:43 +01:00
|
|
|
|
|
|
|
#include "Platform.h"
|
|
|
|
#include "types.h"
|
2014-04-01 02:33:55 +02:00
|
|
|
|
2016-02-01 22:55:43 +01:00
|
|
|
namespace fmt
|
|
|
|
{
|
2016-04-25 12:49:12 +02:00
|
|
|
template<typename... Args>
|
2016-08-03 22:51:05 +02:00
|
|
|
static std::string format(const char*, const Args&...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T, typename>
|
|
|
|
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)
|
2015-01-18 23:54:56 +01:00
|
|
|
{
|
2016-08-03 22:51:05 +02:00
|
|
|
return reinterpret_cast<std::uintptr_t>(&arg);
|
2016-04-25 12:49:12 +02:00
|
|
|
}
|
2016-08-07 15:59:46 +02:00
|
|
|
|
|
|
|
// 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<std::uintptr_t>(&arg);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// This overload resolution takes the precedence
|
|
|
|
static inline u64_wrapper get(T&& arg)
|
|
|
|
{
|
|
|
|
return {std::move(arg)};
|
|
|
|
}
|
2016-08-03 22:51:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct fmt_unveil<T, std::enable_if_t<std::is_integral<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
|
|
|
|
{
|
|
|
|
using type = T;
|
2015-01-18 23:54:56 +01:00
|
|
|
|
2016-08-03 22:51:05 +02:00
|
|
|
static inline u64 get(T arg)
|
2015-01-18 23:54:56 +01:00
|
|
|
{
|
2016-08-03 22:51:05 +02:00
|
|
|
return static_cast<T>(arg);
|
|
|
|
}
|
|
|
|
};
|
2015-01-18 23:54:56 +01:00
|
|
|
|
2016-08-03 22:51:05 +02:00
|
|
|
template<typename T>
|
|
|
|
struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
|
|
|
|
{
|
|
|
|
using type = T;
|
2015-01-18 23:54:56 +01:00
|
|
|
|
2016-08-03 22:51:05 +02:00
|
|
|
// Convert FP to f64 and reinterpret (TODO?)
|
|
|
|
static inline u64 get(f64 arg)
|
2015-01-18 23:54:56 +01:00
|
|
|
{
|
2016-08-03 22:51:05 +02:00
|
|
|
return reinterpret_cast<u64&>(arg);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
|
|
|
{
|
|
|
|
using type = T;
|
|
|
|
|
|
|
|
static inline u64 get(T arg)
|
|
|
|
{
|
|
|
|
return static_cast<std::underlying_type_t<T>>(arg);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct fmt_unveil<T*, void>
|
|
|
|
{
|
|
|
|
using type = const T*;
|
|
|
|
|
|
|
|
static inline u64 get(const T* arg)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<std::uintptr_t>(arg);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T, std::size_t N>
|
|
|
|
struct fmt_unveil<T[N], void>
|
|
|
|
{
|
|
|
|
using type = const T*;
|
|
|
|
|
|
|
|
static inline u64 get(const T* arg)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<std::uintptr_t>(arg);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct fmt_unveil<b8, void>
|
|
|
|
{
|
|
|
|
using type = bool;
|
|
|
|
|
|
|
|
static inline u64 get(const b8& value)
|
|
|
|
{
|
|
|
|
return fmt_unveil<bool>::get(value);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// String type format provider, also type classifier (format() called if an argument is formatted as "%s")
|
|
|
|
template<typename T, typename = void>
|
|
|
|
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<const T*>(static_cast<std::uintptr_t>(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<std::underlying_type_t<T>>(arg);
|
|
|
|
|
|
|
|
// Check narrowing
|
|
|
|
if (static_cast<u64>(value) == arg)
|
|
|
|
{
|
|
|
|
if (const char* str = get(static_cast<T>(value)))
|
|
|
|
{
|
|
|
|
out += str;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fallback to underlying type formatting
|
|
|
|
fmt_class_string<std::underlying_type_t<T>>::format(out, static_cast<u64>(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper constant (may be used in format_enum as lambda return value)
|
|
|
|
static constexpr const char* unknown = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct fmt_class_string<const void*, void>
|
|
|
|
{
|
|
|
|
static void format(std::string& out, u64 arg);
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct fmt_class_string<T*, void> : fmt_class_string<const void*, void>
|
|
|
|
{
|
|
|
|
// Classify all pointers as const void*
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct fmt_class_string<const char*, void>
|
|
|
|
{
|
|
|
|
static void format(std::string& out, u64 arg);
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct fmt_class_string<char*, void> : fmt_class_string<const char*>
|
|
|
|
{
|
|
|
|
// Classify char* as const char*
|
|
|
|
};
|
|
|
|
|
2016-08-05 18:49:45 +02:00
|
|
|
struct fmt_type_info
|
2016-08-03 22:51:05 +02:00
|
|
|
{
|
2016-08-05 18:49:45 +02:00
|
|
|
decltype(&fmt_class_string<int>::format) fmt_string;
|
2016-08-03 22:51:05 +02:00
|
|
|
|
2016-08-05 18:49:45 +02:00
|
|
|
template<typename T>
|
|
|
|
static constexpr fmt_type_info make()
|
2016-08-03 22:51:05 +02:00
|
|
|
{
|
2016-08-05 18:49:45 +02:00
|
|
|
return fmt_type_info
|
2016-08-03 22:51:05 +02:00
|
|
|
{
|
2016-08-05 18:49:45 +02:00
|
|
|
&fmt_class_string<T>::format,
|
|
|
|
};
|
|
|
|
}
|
2016-08-03 22:51:05 +02:00
|
|
|
|
2016-08-05 18:49:45 +02:00
|
|
|
template<typename... Args>
|
|
|
|
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]
|
2015-01-18 23:54:56 +01:00
|
|
|
{
|
2016-08-05 18:49:45 +02:00
|
|
|
make<Args>()...
|
|
|
|
};
|
2016-08-03 22:51:05 +02:00
|
|
|
|
2016-08-05 18:49:45 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename Arg>
|
|
|
|
using fmt_unveil_t = typename fmt_unveil<Arg>::type;
|
|
|
|
|
|
|
|
// Argument array type (each element generated via fmt_unveil<>)
|
|
|
|
template<typename... Args>
|
|
|
|
using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
|
2015-01-18 23:54:56 +01:00
|
|
|
|
2016-08-05 18:49:45 +02:00
|
|
|
namespace fmt
|
|
|
|
{
|
2016-08-03 22:51:05 +02:00
|
|
|
// Internal formatting function
|
2016-08-05 18:49:45 +02:00
|
|
|
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
|
2016-08-03 22:51:05 +02:00
|
|
|
|
|
|
|
// Formatting function
|
|
|
|
template<typename... Args>
|
|
|
|
static SAFE_BUFFERS void append(std::string& out, const char* fmt, const Args&... args)
|
|
|
|
{
|
2016-08-05 18:49:45 +02:00
|
|
|
raw_append(out, fmt, fmt_type_info::get<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
2016-08-03 22:51:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Formatting function
|
|
|
|
template<typename... Args>
|
|
|
|
static SAFE_BUFFERS std::string format(const char* fmt, const Args&... args)
|
2016-02-01 22:55:43 +01:00
|
|
|
{
|
2016-08-03 22:51:05 +02:00
|
|
|
std::string result;
|
|
|
|
append<Args...>(result, fmt, args...);
|
2016-02-01 22:55:43 +01:00
|
|
|
return result;
|
|
|
|
}
|
2016-08-03 22:51:05 +02:00
|
|
|
|
|
|
|
// Internal helper function
|
2016-08-05 18:49:45 +02:00
|
|
|
char* alloc_format(const char*, const fmt_type_info*, const u64*) noexcept;
|
2016-08-03 22:51:05 +02:00
|
|
|
|
|
|
|
// Exception type with formatting constructor
|
|
|
|
template<typename Base>
|
|
|
|
class exception_t : public Base
|
|
|
|
{
|
|
|
|
using base = Base;
|
|
|
|
|
|
|
|
public:
|
|
|
|
template<typename... Args>
|
|
|
|
SAFE_BUFFERS exception_t(const char* fmt, const Args&... args)
|
2016-08-05 18:49:45 +02:00
|
|
|
: base((fmt = alloc_format(fmt, fmt_type_info::get<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...})))
|
2016-08-03 22:51:05 +02:00
|
|
|
{
|
|
|
|
std::free(const_cast<char*>(fmt));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Exception type derived from std::runtime_error with formatting constructor
|
|
|
|
using exception = exception_t<std::runtime_error>;
|
2014-04-01 02:33:55 +02:00
|
|
|
}
|