diff --git a/Makefile b/Makefile index 4f51632a..800b1eab 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,8 @@ endif ifneq (${MU_HACKER},) MESON_FLAGS:=$(MESON_FLAGS) '-Dbuildtype=debug' \ '-Db_sanitize=address' \ - '-Dreadline=enabled' + '-Dreadline=enabled' \ + '-Dcpp_std=c++20' endif .PHONY: all diff --git a/lib/utils/meson.build b/lib/utils/meson.build index ddf526dc..c9dcd249 100644 --- a/lib/utils/meson.build +++ b/lib/utils/meson.build @@ -38,7 +38,9 @@ install: false) lib_mu_utils_dep = declare_dependency( link_with: lib_mu_utils, - include_directories: include_directories(['.', '..']) + compile_args: '-DFMT_HEADER_ONLY', + include_directories: + include_directories(['.', '..', '../thirdparty/fmt']) ) # diff --git a/lib/utils/mu-error.hh b/lib/utils/mu-error.hh index a4b04d4f..9f722b75 100644 --- a/lib/utils/mu-error.hh +++ b/lib/utils/mu-error.hh @@ -28,6 +28,13 @@ #include "mu-utils-format.hh" #include +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif + +#include +#include + namespace Mu { // calculate an error enum value. @@ -76,68 +83,31 @@ struct Error final : public std::exception { /** * Construct an error * - * @param codearg error-code - * @param msgarg the error description + * @param code the error-code + * @param args... libfmt-style format string and parameters */ - Error(Code codearg, const std::string& msgarg) : code_{codearg}, what_{msgarg} {} - Error(Code codearg, std::string&& msgarg) : code_{codearg}, what_{std::move(msgarg)} {} + template + Error(Code code, fmt::format_string frm, T&&... args): + code_{code}, + what_{fmt::format(frm, std::forward(args)...)} {} /** - * Build an error from an error-code and a format string + * Construct an error * - * @param code error-code - * @param frm format string - * @param ... format parameters - * - * @return an Error object + * @param code the error-code + * @param gerr a GError (or {}); the error is _consumed_ by this function + * @param args... libfmt-style format string and parameters */ - __attribute__((format(printf, 3, 0))) Error(Code codearg, const char* frm, ...) - : code_{codearg} - { - va_list args; - va_start(args, frm); - what_ = vformat(frm, args); - va_end(args); - } - - Error(Error&& rhs) = default; - Error(const Error& rhs) = default; + template + Error(Code code, GError **gerr, fmt::format_string frm, T&&... args): + code_{code}, + what_{fmt::format(frm, std::forward(args)...) + + fmt::format(": {}", (gerr && *gerr) ? (*gerr)->message : + "something went wrong")} + { g_clear_error(gerr); } /** - * Build an error from a GError an error-code and a format string - * - * @param code error-code - * @param gerr a GError or {}, which is consumed - * @param frm format string - * @param ... format parameters - * - * @return an Error object - */ - __attribute__((format(printf, 4, 0))) - Error(Code codearg, GError** err, const char* frm, ...) - : code_{codearg} - { - va_list args; - va_start(args, frm); - what_ = vformat(frm, args); - va_end(args); - - if (err && *err) - what_ += format(": %s", (*err)->message); - else - what_ += ": something went wrong"; - - g_clear_error(err); - } - - /** - * DTOR - * - */ - virtual ~Error() override = default; - - /** - * Get the descriptive message. + * Get the descriptive message for this error. * * @return */ @@ -192,8 +162,8 @@ private: return error_domain; } - const Code code_; - std::string what_; + const Code code_; + const std::string what_; }; } // namespace Mu diff --git a/lib/utils/mu-logger.cc b/lib/utils/mu-logger.cc index 632b7cdb..49c2fbf3 100644 --- a/lib/utils/mu-logger.cc +++ b/lib/utils/mu-logger.cc @@ -51,15 +51,13 @@ maybe_open_logfile() const auto logdir{to_string_gchar(g_path_get_dirname(MuLogPath.c_str()))}; if (g_mkdir_with_parents(logdir.c_str(), 0700) != 0) { - std::cerr << "creating " << logdir << " failed:" << g_strerror(errno) - << std::endl; + mu_printerrln("creating {} failed: {}", logdir, g_strerror(errno)); return false; } MuStream.open(MuLogPath, std::ios::out | std::ios::app); if (!MuStream.is_open()) { - std::cerr << "opening " << MuLogPath << " failed:" << g_strerror(errno) - << std::endl; + mu_printerrln("opening {} failed: {}", MuLogPath, g_strerror(errno)); return false; } @@ -86,8 +84,7 @@ maybe_rotate_logfile() MuStream.close(); if (g_rename(MuLogPath.c_str(), old.c_str()) != 0) - std::cerr << "failed to rename " << MuLogPath << " -> " << old.c_str() << ": " - << g_strerror(errno) << std::endl; + mu_printerrln("failed to rename {} -> {}: {}", MuLogPath, old, g_strerror(errno)); return maybe_open_logfile(); } @@ -197,7 +194,7 @@ static void test_logger_threads(void) { const auto testpath{test_random_tmpdir() + "/test.log"}; - g_message("log-file: %s", testpath.c_str()); + mu_message("log-file: {}", testpath); auto logger = Logger::make(testpath.c_str(), Logger::Options::File | Logger::Options::Debug); assert_valid_result(logger); @@ -212,7 +209,7 @@ test_logger_threads(void) threads.emplace_back( std::thread([n,&running]{ while (running) { - g_debug("log message from thread <%d>", n); + mu_debug("log message from thread <{}>", n); std::this_thread::yield(); } })); diff --git a/lib/utils/mu-regex.cc b/lib/utils/mu-regex.cc index 566903bb..ebb5d42d 100644 --- a/lib/utils/mu-regex.cc +++ b/lib/utils/mu-regex.cc @@ -51,15 +51,15 @@ test_regex_replace() int main(int argc, char* argv[]) try { - mu_test_init(&argc, &argv); + mu_test_init(&argc, &argv); - g_test_add_func("/regex/match", test_regex_match); - g_test_add_func("/regex/replace", test_regex_replace); - return g_test_run(); + g_test_add_func("/regex/match", test_regex_match); + g_test_add_func("/regex/replace", test_regex_replace); + return g_test_run(); } catch (const std::runtime_error& re) { - std::cerr << re.what() << "\n"; - return 1; + mu_printerrln("{}", re.what()); + return 1; } #endif /*BUILD_TESTS*/ diff --git a/lib/utils/mu-result.hh b/lib/utils/mu-result.hh index 9f7d719e..428b88a5 100644 --- a/lib/utils/mu-result.hh +++ b/lib/utils/mu-result.hh @@ -39,8 +39,6 @@ template using Result = tl::expected; */ template class Result::expected -// note: "class", not "typename"; -// https://stackoverflow.com/questions/46412754/class-name-injection-and-constructors Ok(T&& t) { return std::move(t); @@ -83,55 +81,21 @@ Err(const Result& res) return res.error(); } - - -template -static inline Result -Ok(const T& t) -{ - if (t) - return Ok(); - else - return Err(t.error()); -} - - /* * convenience */ - -static inline tl::unexpected -Err(Error::Code errcode, std::string&& msg="") +template +inline tl::unexpected +Err(Error::Code code, fmt::format_string frm, T&&... args) { - return Err(Error{errcode, std::move(msg)}); + return Err(Error{code, frm, std::forward(args)...}); } -__attribute__((format(printf, 2, 0))) -static inline tl::unexpected -Err(Error::Code errcode, const char* frm, ...) +template +inline tl::unexpected +Err(Error::Code code, GError **err, fmt::format_string frm, T&&... args) { - va_list args; - va_start(args, frm); - auto str{vformat(frm, args)}; - va_end(args); - - return Err(errcode, std::move(str)); -} - -__attribute__((format(printf, 3, 0))) -static inline tl::unexpected -Err(Error::Code errcode, GError **err, const char* frm, ...) -{ - va_list args; - va_start(args, frm); - auto str{vformat(frm, args)}; - va_end(args); - - if (err && *err) - str += format(" (%s)", (*err)->message ? (*err)->message : ""); - g_clear_error(err); - - return Err(errcode, std::move(str)); + return Err(Error{code, err, frm, std::forward(args)...}); } /** diff --git a/lib/utils/mu-sexp.cc b/lib/utils/mu-sexp.cc index cfc9812e..34ecdfb9 100644 --- a/lib/utils/mu-sexp.cc +++ b/lib/utils/mu-sexp.cc @@ -517,7 +517,7 @@ try { return g_test_run(); } catch (const std::runtime_error& re) { - std::cerr << re.what() << "\n"; + mu_printerrln("{}", re.what()); return 1; } diff --git a/lib/utils/mu-test-utils.cc b/lib/utils/mu-test-utils.cc index 702548f3..fb82b4f7 100644 --- a/lib/utils/mu-test-utils.cc +++ b/lib/utils/mu-test-utils.cc @@ -35,20 +35,17 @@ using namespace Mu; -char* -Mu::test_mu_common_get_random_tmpdir() +std::string +Mu::test_random_tmpdir() { - char* dir; - int res; + auto&& dir = mu_format("{}{}mu-test-{}{}test-{:x}", + g_get_tmp_dir(), + G_DIR_SEPARATOR, + getuid(), + G_DIR_SEPARATOR, + ::random() * getpid() * ::time({})); - dir = g_strdup_printf("%s%cmu-test-%d%ctest-%x", - g_get_tmp_dir(), - G_DIR_SEPARATOR, - getuid(), - G_DIR_SEPARATOR, - (int)random() * getpid() * (int)time(NULL)); - - res = g_mkdir_with_parents(dir, 0700); + auto res = g_mkdir_with_parents(dir.c_str(), 0700); g_assert(res != -1); return dir; @@ -76,12 +73,12 @@ Mu::set_en_us_utf8_locale() setlocale(LC_ALL, "en_US.UTF-8"); if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0) { - g_print("Note: Unit tests require the en_US.utf8 locale. " - "Ignoring test cases.\n"); - return FALSE; + mu_println("Note: Unit tests require the en_US.utf8 locale. " + "Ignoring test cases."); + return false; } - return TRUE; + return true; } static void @@ -108,8 +105,6 @@ Mu::mu_test_init(int *argc, char ***argv) (GLogFunc)black_hole, NULL); } - - void Mu::allow_warnings() { @@ -128,10 +123,8 @@ Mu::TempDir::TempDir(bool autodelete): autodelete_{autodelete} throw Mu::Error(Error::Code::File, &err, "failed to create temporary directory"); - path_ = tmpdir; - g_free(tmpdir); - - g_debug("created '%s'", path_.c_str()); + path_ = to_string_gchar(std::move(tmpdir)); + mu_debug("created '{}'", path_); } Mu::TempDir::~TempDir() diff --git a/lib/utils/mu-test-utils.hh b/lib/utils/mu-test-utils.hh index 2db89b9a..84743bd4 100644 --- a/lib/utils/mu-test-utils.hh +++ b/lib/utils/mu-test-utils.hh @@ -28,11 +28,12 @@ namespace Mu { /** * get a dir name for a random temporary directory to do tests * - * @return a random dir name, g_free when it's no longer needed + * @return a random dir name */ -char* test_mu_common_get_random_tmpdir(void); -static inline std::string test_random_tmpdir() { - return to_string_gchar(test_mu_common_get_random_tmpdir()); +std::string test_random_tmpdir(void); + +inline gchar* test_mu_common_get_random_tmpdir() { + return g_strdup(test_random_tmpdir().c_str()); } /** diff --git a/lib/utils/mu-utils.hh b/lib/utils/mu-utils.hh index 836f3b06..b1bce24c 100644 --- a/lib/utils/mu-utils.hh +++ b/lib/utils/mu-utils.hh @@ -17,8 +17,8 @@ ** 02110-1301, USA. */ -#ifndef __MU_UTILS_HH__ -#define __MU_UTILS_HH__ +#ifndef MU_UTILS_HH__ +#define MU_UTILS_HH__ #include #include @@ -37,8 +37,50 @@ #include "mu-utils-format.hh" #include "mu-option.hh" + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif /*FMT_HEADER_ONLY*/ +#include +#include + namespace Mu { +/* + * Logging functions connect libfmt with the Glib logging system + */ + +template +inline void mu_debug(fmt::format_string frm, T&&... args) noexcept { + g_log("mu", G_LOG_LEVEL_DEBUG, "%s", + fmt::format(frm, std::forward(args)...).c_str()); +} +template +inline void mu_info(fmt::format_string frm, T&&... args) noexcept { + g_log("mu", G_LOG_LEVEL_INFO, "%s", + fmt::format(frm, std::forward(args)...).c_str()); +} +template +inline void mu_message(fmt::format_string frm, T&&... args) noexcept { + g_log("mu", G_LOG_LEVEL_MESSAGE, "%s", + fmt::format(frm, std::forward(args)...).c_str()); +} +template +inline void mu_warning(fmt::format_string frm, T&&... args) noexcept { + g_log("mu", G_LOG_LEVEL_WARNING, "%s", + fmt::format(frm, std::forward(args)...).c_str()); +} +template +inline void mu_critical(fmt::format_string frm, T&&... args) noexcept { + g_log("mu", G_LOG_LEVEL_CRITICAL, "%s", + fmt::format(frm, std::forward(args)...).c_str()); +} +template +inline void mu_error(fmt::format_string frm, T&&... args) noexcept { + g_log("mu", G_LOG_LEVEL_ERROR, "%s", + fmt::format(frm, std::forward(args)...).c_str()); +} + using StringVec = std::vector; /** @@ -517,4 +559,4 @@ private: } // namespace Mu -#endif /* __MU_UTILS_HH__ */ +#endif /* MU_UTILS_HH__ */ diff --git a/mu/tests/test-mu-query.cc b/mu/tests/test-mu-query.cc index 7016b08a..4db678c8 100644 --- a/mu/tests/test-mu-query.cc +++ b/mu/tests/test-mu-query.cc @@ -44,32 +44,27 @@ static std::string DB_PATH2; static std::string make_database(const std::string& testdir) { - char* tmpdir{test_mu_common_get_random_tmpdir()}; + auto&& tmpdir{test_random_tmpdir()}; /* use the env var rather than `--muhome` */ - g_setenv("MUHOME", tmpdir, 1); - const auto cmdline{format("/bin/sh -c '" - "%s --quiet init --maildir=%s ; " - "%s --quiet index'", - MU_PROGRAM, - testdir.c_str(), - MU_PROGRAM)}; + g_setenv("MUHOME", tmpdir.c_str(), 1); + const auto cmdline{mu_format( + "/bin/sh -c '" + "{} --quiet init --maildir={} ; " + "{} --quiet index'", + MU_PROGRAM, testdir, MU_PROGRAM)}; if (g_test_verbose()) g_printerr("\n%s\n", cmdline.c_str()); g_assert(g_spawn_command_line_sync(cmdline.c_str(), NULL, NULL, NULL, NULL)); - auto xpath = g_strdup_printf("%s%c%s", tmpdir, G_DIR_SEPARATOR, "xapian"); - g_free(tmpdir); - + auto xpath = mu_format("{}{}{}", + tmpdir, G_DIR_SEPARATOR, "xapian"); /* ensure MUHOME worked */ - g_assert_cmpuint(::access(xpath, F_OK), ==, 0); + g_assert_cmpuint(::access(xpath.c_str(), F_OK), ==, 0); - std::string dbpath{xpath}; - g_free(xpath); - - return dbpath; + return xpath; } static void