From 5616fd97f4ffdf7850d2dabf8d45dcb942283a04 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 26 Jun 2023 01:17:22 -0600 Subject: [PATCH] common: use compile-time format string checking --- bsputil/bsputil.cc | 2 +- common/bsputils.cc | 2 +- common/entdata.cc | 2 +- common/log.cc | 18 ++++++++++++++++++ include/common/log.hh | 27 +++++++++++++++++---------- qbsp/map.cc | 8 ++++---- 6 files changed, 42 insertions(+), 17 deletions(-) diff --git a/bsputil/bsputil.cc b/bsputil/bsputil.cc index 2ac93bd9..d07b05ea 100644 --- a/bsputil/bsputil.cc +++ b/bsputil/bsputil.cc @@ -508,7 +508,7 @@ bool ParseEntity(parser_t &parser, map_entity_t &entity) } if (parser.token != "{") { - FError("{}: Invalid entity format, { not found", parser.location); + FError("{}: Invalid entity format, {{ not found", parser.location); } do { diff --git a/common/bsputils.cc b/common/bsputils.cc index 6a96a2a6..cd837285 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -548,7 +548,7 @@ void Face_DebugPrint(const mbsp_t *bsp, const mface_t *face) const char *texname = Face_TextureName(bsp, face); logging::print("face {}, texture '{}', {} edges; vectors:\n" - "{: 3.3}\n", + "{}\n", Face_GetNum(bsp, face), texname, face->numedges, tex->vecs); for (int i = 0; i < face->numedges; i++) { diff --git a/common/entdata.cc b/common/entdata.cc index 55d6751a..755af48d 100644 --- a/common/entdata.cc +++ b/common/entdata.cc @@ -149,7 +149,7 @@ void entdict_t::parse(parser_base_t &parser) if (!parser.parse_token()) return; if (parser.token != "{") - FError("found {} when expecting {", parser.token); + FError("found {} when expecting {{", parser.token); /* go through all the keys in this entity */ while (1) { diff --git a/common/log.cc b/common/log.cc index 670d2ffd..8c926e8e 100644 --- a/common/log.cc +++ b/common/log.cc @@ -140,11 +140,23 @@ void print(flag logflag, const char *str) print_mutex.unlock(); } +void vprint(flag logflag, fmt::string_view format, fmt::format_args args) +{ + // see https://fmt.dev/10.0.0/api.html#argument-lists + + print(logflag, fmt::vformat(format, args).c_str()); +} + void print(const char *str) { print(flag::DEFAULT, str); } +void vprint(fmt::string_view format, fmt::format_args args) +{ + vprint(flag::DEFAULT, format, args); +} + static time_point start_time; static bool is_timing = false; static uint64_t last_count = -1; @@ -386,3 +398,9 @@ ericwtools_error::ericwtools_error(const char *what) #endif throw ericwtools_error(error); } + +[[noreturn]] void VError(fmt::string_view format, fmt::format_args args) +{ + auto formatted = fmt::vformat(format, args); + Error(formatted.c_str()); +} diff --git a/include/common/log.hh b/include/common/log.hh index e8f841ce..f9b6ff39 100644 --- a/include/common/log.hh +++ b/include/common/log.hh @@ -73,23 +73,30 @@ void close(); // print to respective targets based on log flag void print(flag logflag, const char *str); +// print to respective targets based on log flag +void vprint(flag logflag, fmt::string_view format, fmt::format_args args); + // print to default targets void print(const char *str); +// print to default targets +void vprint(fmt::string_view format, fmt::format_args args); + // format print to specified targets -template -inline void print(flag type, const char *fmt, const Args &...args) +// see: https://fmt.dev/10.0.0/api.html#argument-lists +template +inline void print(flag type, fmt::format_string format, T &&...args) { if (mask & type) { - print(type, fmt::format(fmt::runtime(fmt), std::forward(args)...).c_str()); + vprint(type, format, fmt::make_format_args(args...)); } } // format print to default targets -template -inline void print(const char *formt, const Args &...args) +template +inline void print(fmt::format_string format, T &&...args) { - print(flag::DEFAULT, fmt::format(fmt::runtime(formt), std::forward(args)...).c_str()); + vprint(flag::DEFAULT, format, fmt::make_format_args(args...)); } // set print callback @@ -209,12 +216,12 @@ public: * lightpreview can catch this to avoid crashing the whole UI. */ [[noreturn]] void Error(const char *error); +[[noreturn]] void VError(fmt::string_view format, fmt::format_args args); -template -[[noreturn]] inline void Error(const char *fmt, const Args &...args) +template +[[noreturn]] inline void Error(fmt::format_string format, T &&...args) { - auto formatted = fmt::format(fmt::runtime(fmt), std::forward(args)...); - Error(formatted.c_str()); + VError(format, fmt::make_format_args(args...)); } #define FError(fmt, ...) Error("{}: " fmt, __func__, ##__VA_ARGS__) diff --git a/qbsp/map.cc b/qbsp/map.cc index 56d59677..59824697 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -2496,7 +2496,7 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, texture_def_ // ericw -- brush primitives if (!parser.parse_token(PARSE_PEEK)) - FError("{}: unexpected EOF after { beginning brush", parser.location); + FError("{}: unexpected EOF after {{ beginning brush", parser.location); if (parser.token == "(") { brush.format = brushformat_t::NORMAL; @@ -2512,7 +2512,7 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, texture_def_ // mandatory if (parser.token != "{") - FError("Brush primitives: expected second { at beginning of brush, got \"{}\"", parser.token); + FError("Brush primitives: expected second {{ at beginning of brush, got \"{}\"", parser.token); } // ericw -- end brush primitives @@ -2675,7 +2675,7 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, texture_def_ if (!parser.parse_token()) FError("Brush primitives: unexpected EOF (no closing brace)"); if (parser.token != "}") - FError("Brush primitives: Expected }, got: {}", parser.token); + FError("Brush primitives: Expected }}, got: {}", parser.token); } // ericw -- end brush primitives @@ -2693,7 +2693,7 @@ bool ParseEntity(parser_t &parser, mapentity_t &entity, texture_def_issues_t &is } if (parser.token != "{") { - FError("{}: Invalid entity format, { not found", parser.location); + FError("{}: Invalid entity format, {{ not found", parser.location); } entity.mapbrushes.clear();