Remove awful stringf system
This commit is contained in:
parent
a3899781dc
commit
fa6155ff47
@ -38,7 +38,7 @@ namespace libbacktrace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void error_callback(void*, const char* msg, int errnum) {
|
void error_callback(void*, const char* msg, int errnum) {
|
||||||
throw std::runtime_error(stringf("Libbacktrace error: %s, code %d\n", msg, errnum));
|
throw std::runtime_error(microfmt::format("Libbacktrace error: {}, code {}\n", msg, errnum));
|
||||||
}
|
}
|
||||||
|
|
||||||
backtrace_state* get_backtrace_state() {
|
backtrace_state* get_backtrace_state() {
|
||||||
|
|||||||
@ -52,18 +52,6 @@
|
|||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
// Placed here instead of utils because it's used by error.hpp and utils.hpp
|
|
||||||
template<typename... T> std::string stringf(T... args) {
|
|
||||||
int length = std::snprintf(nullptr, 0, args...);
|
|
||||||
if(length < 0) {
|
|
||||||
throw std::logic_error("invalid arguments to stringf");
|
|
||||||
}
|
|
||||||
std::string str(length, 0);
|
|
||||||
// .data is const char* in c++11, but &str[0] should be legal
|
|
||||||
std::snprintf(&str[0], length + 1, args...);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const stacktrace_frame null_frame {
|
static const stacktrace_frame null_frame {
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@ -17,7 +17,7 @@ namespace detail {
|
|||||||
~dbghelp_syminit_manager() {
|
~dbghelp_syminit_manager() {
|
||||||
for(auto handle : set) {
|
for(auto handle : set) {
|
||||||
if(!SymCleanup(handle)) {
|
if(!SymCleanup(handle)) {
|
||||||
ASSERT(false, stringf("Cpptrace SymCleanup failed with code %llu\n", to_ull(GetLastError())));
|
ASSERT(false, microfmt::format("Cpptrace SymCleanup failed with code {}\n", GetLastError()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ namespace detail {
|
|||||||
void init(HANDLE proc) {
|
void init(HANDLE proc) {
|
||||||
if(set.count(proc) == 0) {
|
if(set.count(proc) == 0) {
|
||||||
if(!SymInitialize(proc, NULL, TRUE)) {
|
if(!SymInitialize(proc, NULL, TRUE)) {
|
||||||
throw std::logic_error(stringf("SymInitialize failed %llu", to_ull(GetLastError())));
|
throw std::logic_error(microfmt::format("SymInitialize failed {}", GetLastError()));
|
||||||
}
|
}
|
||||||
set.insert(proc);
|
set.insert(proc);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ namespace libdwarf {
|
|||||||
char* msg = dwarf_errmsg(error);
|
char* msg = dwarf_errmsg(error);
|
||||||
(void)dbg;
|
(void)dbg;
|
||||||
// dwarf_dealloc_error(dbg, error);
|
// dwarf_dealloc_error(dbg, error);
|
||||||
throw std::runtime_error(stringf("Cpptrace dwarf error %u %s\n", ev, msg));
|
throw std::runtime_error(microfmt::format("Cpptrace dwarf error {} {}\n", ev, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct die_object {
|
struct die_object {
|
||||||
@ -235,7 +235,7 @@ namespace libdwarf {
|
|||||||
return die_object(dbg, target);
|
return die_object(dbg, target);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
PANIC(stringf("unknown form for attribute %d %d\n", attr_num, form));
|
PANIC(microfmt::format("unknown form for attribute {} {}\n", attr_num, form));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
#include "microfmt.hpp"
|
||||||
|
|
||||||
#if IS_MSVC
|
#if IS_MSVC
|
||||||
#define CPPTRACE_PFUNC __FUNCSIG__
|
#define CPPTRACE_PFUNC __FUNCSIG__
|
||||||
@ -57,8 +58,8 @@ namespace detail {
|
|||||||
const char* name = assert_names[static_cast<std::underlying_type<assert_type>::type>(type)];
|
const char* name = assert_names[static_cast<std::underlying_type<assert_type>::type>(type)];
|
||||||
if(message == "") {
|
if(message == "") {
|
||||||
throw std::logic_error(
|
throw std::logic_error(
|
||||||
stringf(
|
microfmt::format(
|
||||||
"Cpptrace %s failed at %s:%d: %s\n"
|
"Cpptrace {} failed at {}:{}: {}\n"
|
||||||
" %s(%s);\n",
|
" %s(%s);\n",
|
||||||
action, location.file, location.line, signature,
|
action, location.file, location.line, signature,
|
||||||
name, expression
|
name, expression
|
||||||
@ -66,8 +67,8 @@ namespace detail {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw std::logic_error(
|
throw std::logic_error(
|
||||||
stringf(
|
microfmt::format(
|
||||||
"Cpptrace %s failed at %s:%d: %s: %s\n"
|
"Cpptrace {} failed at {}:{}: {}: {}\n"
|
||||||
" %s(%s);\n",
|
" %s(%s);\n",
|
||||||
action, location.file, location.line, signature, message.c_str(),
|
action, location.file, location.line, signature, message.c_str(),
|
||||||
name, expression
|
name, expression
|
||||||
@ -83,15 +84,15 @@ namespace detail {
|
|||||||
) {
|
) {
|
||||||
if(message == "") {
|
if(message == "") {
|
||||||
throw std::logic_error(
|
throw std::logic_error(
|
||||||
stringf(
|
microfmt::format(
|
||||||
"Cpptrace panic %s:%d: %s\n",
|
"Cpptrace panic {}:{}: {}\n",
|
||||||
location.file, location.line, signature
|
location.file, location.line, signature
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw std::logic_error(
|
throw std::logic_error(
|
||||||
stringf(
|
microfmt::format(
|
||||||
"Cpptrace panic %s:%d: %s: %s\n",
|
"Cpptrace panic {}:{}: {}: {}\n",
|
||||||
location.file, location.line, signature, message.c_str()
|
location.file, location.line, signature, message.c_str()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
316
src/utils/microfmt.hpp
Normal file
316
src/utils/microfmt.hpp
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
#ifndef MICROFMT_HPP
|
||||||
|
#define MICROFMT_HPP
|
||||||
|
|
||||||
|
// Copyright (c) 2024 Jeremy Rifkin; MIT License
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
|
||||||
|
#include <string_view>
|
||||||
|
#endif
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// {[[align][width]:[fill][base]]}
|
||||||
|
// width: number or {}
|
||||||
|
|
||||||
|
namespace microfmt {
|
||||||
|
namespace detail {
|
||||||
|
#define STR2(x) #x
|
||||||
|
#define STR(x) STR2(x)
|
||||||
|
#define MICROFMT_ASSERT(expr) if(!(expr)) { \
|
||||||
|
throw std::runtime_error("Microfmt check failed" __FILE__ ":" STR(__LINE__) ": " #expr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
inline std::uint64_t clz(std::uint64_t value) {
|
||||||
|
unsigned long out = 0;
|
||||||
|
#ifdef _WIN64
|
||||||
|
_BitScanForward64(&out, value);
|
||||||
|
#else
|
||||||
|
if(_BitScanForward(&out, std::uint32_t(value >> 32))) {
|
||||||
|
return 63 ^ int(out + 32);
|
||||||
|
}
|
||||||
|
_BitScanForward(&out, std::uint32_t(value));
|
||||||
|
#endif
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline std::uint64_t clz(std::uint64_t value) {
|
||||||
|
return __builtin_clzll(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class alignment { left, right };
|
||||||
|
|
||||||
|
struct format_options {
|
||||||
|
alignment align = alignment::left;
|
||||||
|
char fill = ' ';
|
||||||
|
size_t width = 0;
|
||||||
|
char base = 'd';
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename It> void do_write(std::string& out, It begin, It end, const format_options& options) {
|
||||||
|
auto size = end - begin;
|
||||||
|
MICROFMT_ASSERT(size >= 0);
|
||||||
|
if(static_cast<std::size_t>(size) >= options.width) {
|
||||||
|
out.append(begin, end);
|
||||||
|
} else {
|
||||||
|
auto out_size = out.size();
|
||||||
|
out.resize(out_size + options.width);
|
||||||
|
if(options.align == alignment::left) {
|
||||||
|
std::copy(begin, end, out.begin() + out_size);
|
||||||
|
std::fill(out.begin() + out_size + size, out.end(), options.fill);
|
||||||
|
} else {
|
||||||
|
std::fill(out.begin() + out_size, out.begin() + out_size + (options.width - size), options.fill);
|
||||||
|
std::copy(begin, end, out.begin() + out_size + (options.width - size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int shift, int mask>
|
||||||
|
std::string to_string(std::uint64_t value, const char* digits = "0123456789abcdef") {
|
||||||
|
if(value == 0) {
|
||||||
|
return "0";
|
||||||
|
} else {
|
||||||
|
// digits = floor(1 + log_base(x))
|
||||||
|
// log_base(x) = log_2(x) / log_2(base)
|
||||||
|
// log_2(x) == 63 - clz(x)
|
||||||
|
// 1 + (63 - clz(value)) / (63 - clz(1 << shift))
|
||||||
|
// 63 - clz(1 << shift) is the same as shift
|
||||||
|
auto n_digits = 1 + (63 - clz(value)) / shift;
|
||||||
|
std::string number;
|
||||||
|
number.resize(n_digits);
|
||||||
|
std::size_t i = n_digits - 1;
|
||||||
|
while(value > 0) {
|
||||||
|
number[i--] = digits[value & mask];
|
||||||
|
value >>= shift;
|
||||||
|
}
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string to_string(std::uint64_t value, const format_options& options) {
|
||||||
|
switch(options.base) {
|
||||||
|
case 'd': return std::to_string(value);
|
||||||
|
case 'H': return to_string<4, 0xf>(value, "0123456789ABCDEF");
|
||||||
|
case 'h': return to_string<4, 0xf>(value);
|
||||||
|
case 'o': return to_string<3, 0x7>(value);
|
||||||
|
case 'b': return to_string<1, 0x1>(value);
|
||||||
|
default:
|
||||||
|
MICROFMT_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class format_value {
|
||||||
|
enum class value_type {
|
||||||
|
char_value,
|
||||||
|
int64_value,
|
||||||
|
uint64_value,
|
||||||
|
string_value,
|
||||||
|
#if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
|
||||||
|
string_view_value,
|
||||||
|
#endif
|
||||||
|
c_string_value,
|
||||||
|
};
|
||||||
|
union {
|
||||||
|
char char_value;
|
||||||
|
std::int64_t int64_value;
|
||||||
|
std::uint64_t uint64_value;
|
||||||
|
const std::string* string_value;
|
||||||
|
#if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
|
||||||
|
std::string_view string_view_value;
|
||||||
|
#endif
|
||||||
|
const char* c_string_value;
|
||||||
|
};
|
||||||
|
value_type value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_value(char c) : char_value(c), value(value_type::char_value) {}
|
||||||
|
format_value(short int_val) : int64_value(int_val), value(value_type::int64_value) {}
|
||||||
|
format_value(int int_val) : int64_value(int_val), value(value_type::int64_value) {}
|
||||||
|
format_value(long int_val) : int64_value(int_val), value(value_type::int64_value) {}
|
||||||
|
format_value(long long int_val) : int64_value(int_val), value(value_type::int64_value) {}
|
||||||
|
format_value(unsigned char int_val) : uint64_value(int_val), value(value_type::uint64_value) {}
|
||||||
|
format_value(unsigned short int_val) : uint64_value(int_val), value(value_type::uint64_value) {}
|
||||||
|
format_value(unsigned int int_val) : uint64_value(int_val), value(value_type::uint64_value) {}
|
||||||
|
format_value(unsigned long int_val) : uint64_value(int_val), value(value_type::uint64_value) {}
|
||||||
|
format_value(unsigned long long int_val) : uint64_value(int_val), value(value_type::uint64_value) {}
|
||||||
|
format_value(const std::string& string) : string_value(&string), value(value_type::string_value) {}
|
||||||
|
#if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
|
||||||
|
format_value(std::string_view sv) : string_view_value(sv), value(value_type::string_view_value) {}
|
||||||
|
#endif
|
||||||
|
format_value(const char* c_string) : c_string_value(c_string), value(value_type::c_string_value) {}
|
||||||
|
|
||||||
|
int unwrap_int() const {
|
||||||
|
switch(value) {
|
||||||
|
case value_type::int64_value: return static_cast<int>(int64_value);
|
||||||
|
case value_type::uint64_value: return static_cast<int>(uint64_value);
|
||||||
|
default: MICROFMT_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void write(std::string& out, const format_options& options) const {
|
||||||
|
switch(value) {
|
||||||
|
case value_type::char_value:
|
||||||
|
do_write(out, &char_value, &char_value + 1, options);
|
||||||
|
break;
|
||||||
|
case value_type::int64_value:
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
std::int64_t val = int64_value;
|
||||||
|
if(val < 0) {
|
||||||
|
str += '-';
|
||||||
|
val *= -1;
|
||||||
|
}
|
||||||
|
str += to_string(static_cast<std::uint64_t>(val), options);
|
||||||
|
do_write(out, str.begin(), str.end(), options);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case value_type::uint64_value:
|
||||||
|
{
|
||||||
|
std::string str = to_string(uint64_value, options);
|
||||||
|
do_write(out, str.begin(), str.end(), options);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case value_type::string_value:
|
||||||
|
do_write(out, string_value->begin(), string_value->end(), options);
|
||||||
|
break;
|
||||||
|
#if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
|
||||||
|
case value_type::string_view_value:
|
||||||
|
do_write(out, string_view_value.begin(), string_view_value.end(), options);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case value_type::c_string_value:
|
||||||
|
do_write(out, c_string_value, c_string_value + std::strlen(c_string_value), options);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MICROFMT_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline int parse_int(std::string::const_iterator begin, std::string::const_iterator end) {
|
||||||
|
int x = 0;
|
||||||
|
for(auto it = begin; it != end; it++) {
|
||||||
|
MICROFMT_ASSERT(isdigit(*it));
|
||||||
|
x *= 10;
|
||||||
|
x += *it - '0';
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
std::string format(const std::string& format_string, std::array<format_value, N> args) {
|
||||||
|
std::string str;
|
||||||
|
std::size_t arg_i = 0;
|
||||||
|
auto it = format_string.begin();
|
||||||
|
auto peek = [&] (std::size_t dist = 1) -> char { // 0 on failure
|
||||||
|
if(it != format_string.end()) {
|
||||||
|
return *(it + dist);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto read_number = [&] () -> int { // -1 on failure
|
||||||
|
auto scan = it;
|
||||||
|
while(scan != format_string.end() && isdigit(*scan)) {
|
||||||
|
scan++;
|
||||||
|
}
|
||||||
|
if(scan != it) {
|
||||||
|
int val = parse_int(it, scan);
|
||||||
|
it = scan;
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
while(it != format_string.end()) {
|
||||||
|
if(*it == '{') {
|
||||||
|
if(peek() == '{') {
|
||||||
|
// try to handle escape
|
||||||
|
str += '{';
|
||||||
|
it++;
|
||||||
|
} else {
|
||||||
|
// parse format string
|
||||||
|
it++;
|
||||||
|
MICROFMT_ASSERT(it != format_string.end());
|
||||||
|
format_options options;
|
||||||
|
// try to parse alignment
|
||||||
|
if(*it == '<' || *it == '>') {
|
||||||
|
options.align = *it == '<' ? alignment::left : alignment::right;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
// try to parse width
|
||||||
|
auto width = read_number();
|
||||||
|
if(width != -1) {
|
||||||
|
options.width = width;
|
||||||
|
} else if(*it == '{') { // try to parse variable width
|
||||||
|
MICROFMT_ASSERT(peek() == '}');
|
||||||
|
it += 2;
|
||||||
|
MICROFMT_ASSERT(arg_i < N);
|
||||||
|
options.width = args[arg_i++].unwrap_int();
|
||||||
|
}
|
||||||
|
// try to parse fill/base
|
||||||
|
if(*it == ':') {
|
||||||
|
it++;
|
||||||
|
// try to parse fill
|
||||||
|
if(*it != '}' && peek(1) != '}') {
|
||||||
|
// two chars before the }, treat as fill+base
|
||||||
|
options.fill = *it++;
|
||||||
|
options.base = *it++;
|
||||||
|
} else if(*it != '}') {
|
||||||
|
// one char before the }, treat as base if possible
|
||||||
|
if(*it == 'd' || *it == 'h' || *it == 'H' || *it == 'o' || *it == 'b') {
|
||||||
|
options.base = *it++;
|
||||||
|
} else {
|
||||||
|
options.fill = *it++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MICROFMT_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MICROFMT_ASSERT(*it == '}');
|
||||||
|
MICROFMT_ASSERT(arg_i < N);
|
||||||
|
args[arg_i++].write(str, options);
|
||||||
|
}
|
||||||
|
} else if(*it == '}') {
|
||||||
|
// parse }} escape
|
||||||
|
if(peek() == '}') {
|
||||||
|
str += '}';
|
||||||
|
it++;
|
||||||
|
} else {
|
||||||
|
MICROFMT_ASSERT(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str += *it;
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
std::string format(const std::string& format_string, Args... args) {
|
||||||
|
return detail::format<sizeof...(args)>(format_string, {detail::format_value(args)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void print(const std::string& format_string, Args... args) {
|
||||||
|
std::cout<<format(format_string, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void print(std::ostream& ostream, const std::string& format_string, Args... args) {
|
||||||
|
ostream<<format(format_string, args...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
|
#include "microfmt.hpp"
|
||||||
|
|
||||||
#if IS_WINDOWS
|
#if IS_WINDOWS
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user