lib: remove mu-runtime
Remove runtime; update the logger for that, and move the runtime-paths stuff to utils.
This commit is contained in:
@ -29,7 +29,6 @@ lib_mu=static_library(
|
|||||||
'mu-query-match-deciders.cc',
|
'mu-query-match-deciders.cc',
|
||||||
'mu-query-threads.cc',
|
'mu-query-threads.cc',
|
||||||
'mu-query.cc',
|
'mu-query.cc',
|
||||||
'mu-runtime.cc',
|
|
||||||
'mu-script.cc',
|
'mu-script.cc',
|
||||||
'mu-server.cc',
|
'mu-server.cc',
|
||||||
'mu-store.cc',
|
'mu-store.cc',
|
||||||
|
|||||||
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
** Copyright (C) 2019-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
||||||
**
|
|
||||||
** This program is free software; you can redistribute it and/or modify it
|
|
||||||
** under the terms of the GNU General Public License as published by the
|
|
||||||
** Free Software Foundation; either version 3, or (at your option) any
|
|
||||||
** later version.
|
|
||||||
**
|
|
||||||
** This program is distributed in the hope that it will be useful,
|
|
||||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
** GNU General Public License for more details.
|
|
||||||
**
|
|
||||||
** You should have received a copy of the GNU General Public License
|
|
||||||
** along with this program; if not, write to the Free Software Foundation,
|
|
||||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mu-runtime.hh"
|
|
||||||
#include "utils/mu-util.h"
|
|
||||||
#include "utils/mu-logger.hh"
|
|
||||||
|
|
||||||
#include <locale.h> /* for setlocale() */
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
static std::unordered_map<MuRuntimePath, std::string> RuntimePaths;
|
|
||||||
|
|
||||||
constexpr auto PartsDir = "parts";
|
|
||||||
constexpr auto LogDir = "log";
|
|
||||||
constexpr auto XapianDir = "xapian";
|
|
||||||
constexpr auto MuName = "mu";
|
|
||||||
constexpr auto Bookmarks = "bookmarks";
|
|
||||||
|
|
||||||
static const std::string Sepa{G_DIR_SEPARATOR_S};
|
|
||||||
|
|
||||||
static void
|
|
||||||
init_paths_xdg()
|
|
||||||
{
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_XAPIANDB,
|
|
||||||
g_get_user_cache_dir() + Sepa + MuName + Sepa + XapianDir);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_CACHE, g_get_user_cache_dir() + Sepa + MuName);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_MIMECACHE,
|
|
||||||
g_get_user_cache_dir() + Sepa + MuName + Sepa + PartsDir);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_LOGDIR, g_get_user_cache_dir() + Sepa + MuName);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_BOOKMARKS, g_get_user_config_dir() + Sepa + MuName);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
init_paths_muhome(const char* muhome)
|
|
||||||
{
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_XAPIANDB, muhome + Sepa + XapianDir);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_CACHE, muhome);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_MIMECACHE, muhome + Sepa + PartsDir);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_LOGDIR, muhome + Sepa + LogDir);
|
|
||||||
RuntimePaths.emplace(MU_RUNTIME_PATH_BOOKMARKS, muhome + Sepa + Bookmarks);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
mu_runtime_init(const char* muhome, const char* name, gboolean debug)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(RuntimePaths.empty(), FALSE);
|
|
||||||
g_return_val_if_fail(name, FALSE);
|
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
|
||||||
|
|
||||||
if (muhome)
|
|
||||||
init_paths_muhome(muhome);
|
|
||||||
else
|
|
||||||
init_paths_xdg();
|
|
||||||
|
|
||||||
for (const auto& d : RuntimePaths) {
|
|
||||||
char* dir;
|
|
||||||
if (d.first == MU_RUNTIME_PATH_BOOKMARKS) // special case
|
|
||||||
dir = g_path_get_dirname(d.second.c_str());
|
|
||||||
else
|
|
||||||
dir = g_strdup(d.second.c_str());
|
|
||||||
|
|
||||||
auto ok = mu_util_create_dir_maybe(dir, 0700, TRUE);
|
|
||||||
if (!ok) {
|
|
||||||
g_critical("failed to create %s", dir);
|
|
||||||
g_free(dir);
|
|
||||||
mu_runtime_uninit();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
g_free(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto log_path = RuntimePaths[MU_RUNTIME_PATH_LOGDIR] + Sepa + name + ".log";
|
|
||||||
|
|
||||||
using namespace Mu;
|
|
||||||
LogOptions opts{LogOptions::None};
|
|
||||||
if (debug)
|
|
||||||
opts |= (LogOptions::Debug | LogOptions::None);
|
|
||||||
|
|
||||||
Mu::log_init(log_path, opts);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mu_runtime_uninit(void)
|
|
||||||
{
|
|
||||||
RuntimePaths.clear();
|
|
||||||
Mu::log_uninit();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char*
|
|
||||||
mu_runtime_path(MuRuntimePath path)
|
|
||||||
{
|
|
||||||
const auto it = RuntimePaths.find(path);
|
|
||||||
if (it == RuntimePaths.end())
|
|
||||||
return NULL;
|
|
||||||
else
|
|
||||||
return it->second.c_str();
|
|
||||||
}
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
||||||
**
|
|
||||||
** Copyright (C) 2012-2019 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
||||||
**
|
|
||||||
** This program is free software; you can redistribute it and/or modify it
|
|
||||||
** under the terms of the GNU General Public License as published by the
|
|
||||||
** Free Software Foundation; either version 3, or (at your option) any
|
|
||||||
** later version.
|
|
||||||
**
|
|
||||||
** This program is distributed in the hope that it will be useful,
|
|
||||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
** GNU General Public License for more details.
|
|
||||||
**
|
|
||||||
** You should have received a copy of the GNU General Public License
|
|
||||||
** along with this program; if not, write to the Free Software Foundation,
|
|
||||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
#ifndef __MU_RUNTIME_H__
|
|
||||||
#define __MU_RUNTIME_H__
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* initialize the mu runtime system; initializes logging and other
|
|
||||||
* systems. To uninitialize, use mu_runtime_uninit
|
|
||||||
*
|
|
||||||
* @param muhome path where to find the mu home directory (typically, ~/.cache/mu)
|
|
||||||
* @param name of the main program, ie. 'mu', 'mug' or
|
|
||||||
* 'procmule'. this influences the name of the e.g. the logfile
|
|
||||||
* @param debug debug-mode
|
|
||||||
*
|
|
||||||
* @return TRUE if succeeded, FALSE in case of error
|
|
||||||
*/
|
|
||||||
gboolean mu_runtime_init(const char* muhome, const char* name, gboolean debug);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* free all resources
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void mu_runtime_uninit(void);
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
MU_RUNTIME_PATH_XAPIANDB, /* mu xapian db path */
|
|
||||||
MU_RUNTIME_PATH_BOOKMARKS, /* mu bookmarks file path */
|
|
||||||
MU_RUNTIME_PATH_CACHE, /* mu cache path */
|
|
||||||
MU_RUNTIME_PATH_MIMECACHE, /* mu cache path for attachments etc. */
|
|
||||||
MU_RUNTIME_PATH_LOGDIR, /* mu path for log files */
|
|
||||||
|
|
||||||
MU_RUNTIME_PATH_NUM
|
|
||||||
} MuRuntimePath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get a file system path to some 'special' file or directory
|
|
||||||
*
|
|
||||||
* @return ma string which should be not be modified/freed, or NULL in
|
|
||||||
* case of error.
|
|
||||||
*/
|
|
||||||
const char* mu_runtime_path(MuRuntimePath path);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /*__MU_RUNTIME_H__*/
|
|
||||||
@ -34,7 +34,6 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
|
|
||||||
#include "mu-runtime.hh"
|
|
||||||
#include "mu-maildir.hh"
|
#include "mu-maildir.hh"
|
||||||
#include "mu-query.hh"
|
#include "mu-query.hh"
|
||||||
#include "index/mu-indexer.hh"
|
#include "index/mu-indexer.hh"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
** Copyright (C) 2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
** Copyright (C) 2020-2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||||
**
|
**
|
||||||
** This program is free software; you can redistribute it and/or modify it
|
** This program is free software; you can redistribute it and/or modify it
|
||||||
** under the terms of the GNU General Public License as published by the
|
** under the terms of the GNU General Public License as published by the
|
||||||
@ -32,10 +32,10 @@
|
|||||||
|
|
||||||
using namespace Mu;
|
using namespace Mu;
|
||||||
|
|
||||||
static bool MuLogInitialized = false;
|
static bool MuLogInitialized = false;
|
||||||
static Mu::LogOptions MuLogOptions;
|
static Mu::Logger::Options MuLogOptions;
|
||||||
static std::ofstream MuStream;
|
static std::ofstream MuStream;
|
||||||
static auto MaxLogFileSize = 1000 * 1024;
|
static auto MaxLogFileSize = 1000 * 1024;
|
||||||
|
|
||||||
static std::string MuLogPath;
|
static std::string MuLogPath;
|
||||||
|
|
||||||
@ -114,16 +114,21 @@ log_journal(GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpoin
|
|||||||
return g_log_writer_journald(level, fields, n_fields, user_data);
|
return g_log_writer_journald(level, fields, n_fields, user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Mu::log_init(const std::string& path, Mu::LogOptions opts)
|
|
||||||
{
|
|
||||||
if (MuLogInitialized) {
|
|
||||||
g_error("logging is already initialized");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Result<Logger>
|
||||||
|
Mu::Logger::make(const std::string& path, Mu::Logger::Options opts)
|
||||||
|
{
|
||||||
|
if (MuLogInitialized)
|
||||||
|
return Err(Error::Code::Internal, "logging already initialized");
|
||||||
|
|
||||||
|
return Ok(Logger(path, opts));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Mu::Logger::Logger(const std::string& path, Mu::Logger::Options opts)
|
||||||
|
{
|
||||||
if (g_getenv("MU_LOG_STDOUTERR"))
|
if (g_getenv("MU_LOG_STDOUTERR"))
|
||||||
opts |= LogOptions::StdOutErr;
|
opts |= Logger::Options::StdOutErr;
|
||||||
|
|
||||||
MuLogOptions = opts;
|
MuLogOptions = opts;
|
||||||
MuLogPath = path;
|
MuLogPath = path;
|
||||||
@ -132,12 +137,12 @@ Mu::log_init(const std::string& path, Mu::LogOptions opts)
|
|||||||
[](GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data) {
|
[](GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data) {
|
||||||
// filter out debug-level messages?
|
// filter out debug-level messages?
|
||||||
if (level == G_LOG_LEVEL_DEBUG &&
|
if (level == G_LOG_LEVEL_DEBUG &&
|
||||||
(none_of(MuLogOptions & Mu::LogOptions::Debug)))
|
(none_of(MuLogOptions & Options::Debug)))
|
||||||
return G_LOG_WRITER_HANDLED;
|
return G_LOG_WRITER_HANDLED;
|
||||||
|
|
||||||
// log criticals to stdout / err or if asked
|
// log criticals to stdout / err or if asked
|
||||||
if (level == G_LOG_LEVEL_CRITICAL ||
|
if (level == G_LOG_LEVEL_CRITICAL ||
|
||||||
any_of(MuLogOptions & Mu::LogOptions::StdOutErr)) {
|
any_of(MuLogOptions & Options::StdOutErr)) {
|
||||||
log_stdouterr(level, fields, n_fields, user_data);
|
log_stdouterr(level, fields, n_fields, user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,14 +156,13 @@ Mu::log_init(const std::string& path, Mu::LogOptions opts)
|
|||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
g_message("logging initialized; debug: %s, stdout/stderr: %s",
|
g_message("logging initialized; debug: %s, stdout/stderr: %s",
|
||||||
any_of(log_get_options() & LogOptions::Debug) ? "yes" : "no",
|
any_of(opts & Options::Debug) ? "yes" : "no",
|
||||||
any_of(log_get_options() & LogOptions::StdOutErr) ? "yes" : "no");
|
any_of(opts & Options::StdOutErr) ? "yes" : "no");
|
||||||
|
|
||||||
MuLogInitialized = true;
|
MuLogInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Logger::~Logger()
|
||||||
Mu::log_uninit()
|
|
||||||
{
|
{
|
||||||
if (!MuLogInitialized)
|
if (!MuLogInitialized)
|
||||||
return;
|
return;
|
||||||
@ -168,15 +172,3 @@ Mu::log_uninit()
|
|||||||
|
|
||||||
MuLogInitialized = false;
|
MuLogInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Mu::log_set_options(Mu::LogOptions opts)
|
|
||||||
{
|
|
||||||
MuLogOptions = opts;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mu::LogOptions
|
|
||||||
Mu::log_get_options()
|
|
||||||
{
|
|
||||||
return MuLogOptions;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
** Copyright (C) 2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
** Copyright (C) 2020-2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||||
**
|
**
|
||||||
** This program is free software; you can redistribute it and/or modify it
|
** This program is free software; you can redistribute it and/or modify it
|
||||||
** under the terms of the GNU General Public License as published by the
|
** under the terms of the GNU General Public License as published by the
|
||||||
@ -21,54 +21,54 @@
|
|||||||
#define MU_LOGGER_HH__
|
#define MU_LOGGER_HH__
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "utils/mu-utils.hh"
|
#include <utils/mu-utils.hh>
|
||||||
|
#include <utils/mu-result.hh>
|
||||||
|
|
||||||
namespace Mu {
|
namespace Mu {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logging options
|
* RAII object for handling logging (through g_(debug|warning|...))
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
enum struct LogOptions {
|
struct Logger {
|
||||||
None = 0, /**< Nothing specific */
|
|
||||||
StdOutErr = 1 << 1, /**< Log to stdout/stderr */
|
/**
|
||||||
Debug = 1 << 2, /**< Include debug-level logs */
|
* Logging options
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum struct Options {
|
||||||
|
None = 0, /**< Nothing specific */
|
||||||
|
StdOutErr = 1 << 1, /**< Log to stdout/stderr */
|
||||||
|
Debug = 1 << 2, /**< Include debug-level logs */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the logging system.
|
||||||
|
*
|
||||||
|
* Note that the path is only used if structured logging fails --
|
||||||
|
* practically, it goes to the file if there's no systemd/journald.
|
||||||
|
*
|
||||||
|
* if the environment variable MU_LOG_STDOUTERR is set,
|
||||||
|
* LogOptions::StdoutErr is implied.
|
||||||
|
*
|
||||||
|
* @param path path to the log file
|
||||||
|
* @param opts logging options
|
||||||
|
*/
|
||||||
|
static Result<Logger> make(const std::string& path, Options opts=Options::None);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTOR
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
~Logger();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger(const std::string& path, Options opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
MU_ENABLE_BITOPS(Logger::Options);
|
||||||
* Initialize the logging system. Note that the path is only used if structured
|
|
||||||
* logging fails -- practically, it goes to the file if there's
|
|
||||||
* systemd/journald.
|
|
||||||
*
|
|
||||||
* if the environment variable MU_LOG_STDOUTERR is set, LogOptions::StdoutErr is
|
|
||||||
* implied.
|
|
||||||
*
|
|
||||||
* @param path path to the log file
|
|
||||||
* @param opts logging options
|
|
||||||
*/
|
|
||||||
void log_init(const std::string& path, LogOptions opts);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uninitialize the logging system
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void log_uninit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the logging options.
|
|
||||||
*
|
|
||||||
* @param opts options
|
|
||||||
*/
|
|
||||||
void log_set_options(LogOptions opts);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current log options
|
|
||||||
*
|
|
||||||
* @return the log options
|
|
||||||
*/
|
|
||||||
LogOptions log_get_options();
|
|
||||||
|
|
||||||
} // namespace Mu
|
} // namespace Mu
|
||||||
MU_ENABLE_BITOPS(Mu::LogOptions);
|
|
||||||
|
|
||||||
#endif /* MU_LOGGER_HH__ */
|
#endif /* MU_LOGGER_HH__ */
|
||||||
|
|||||||
@ -638,3 +638,36 @@ Mu::timezone_available(const std::string& tz)
|
|||||||
|
|
||||||
return have_tz;
|
return have_tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Mu::runtime_path(Mu::RuntimePath path, const std::string& muhome)
|
||||||
|
{
|
||||||
|
auto [mu_cache, mu_config] =
|
||||||
|
std::invoke([&]()->std::pair<std::string, std::string> {
|
||||||
|
|
||||||
|
static std::string mu{"/mu"};
|
||||||
|
if (muhome.empty())
|
||||||
|
return { g_get_user_cache_dir() + mu,
|
||||||
|
g_get_user_config_dir() + mu };
|
||||||
|
else
|
||||||
|
return { muhome, muhome };
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (path) {
|
||||||
|
case Mu::RuntimePath::Cache:
|
||||||
|
return mu_cache;
|
||||||
|
case Mu::RuntimePath::XapianDb:
|
||||||
|
return mu_cache + "/xapian";
|
||||||
|
case Mu::RuntimePath::LogFile:
|
||||||
|
return mu_cache + "/mu.log";
|
||||||
|
case Mu::RuntimePath::Bookmarks:
|
||||||
|
return mu_config + "/bookmarks";
|
||||||
|
case Mu::RuntimePath::Config:
|
||||||
|
return mu_config;
|
||||||
|
case Mu::RuntimePath::Scripts:
|
||||||
|
return mu_config + "/scripts";
|
||||||
|
default:
|
||||||
|
throw std::logic_error("unknown path");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -175,6 +175,31 @@ bool timezone_available(const std::string& tz);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Well-known runtime paths
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum struct RuntimePath {
|
||||||
|
XapianDb,
|
||||||
|
Cache,
|
||||||
|
LogFile,
|
||||||
|
Config,
|
||||||
|
Scripts,
|
||||||
|
Bookmarks
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get some well-known Path for internal use when don't have
|
||||||
|
* access to the command-line
|
||||||
|
*
|
||||||
|
* @param path the RuntimePath to find
|
||||||
|
* @param muhome path to muhome directory, or empty for the default.
|
||||||
|
*
|
||||||
|
* @return the path name
|
||||||
|
*/
|
||||||
|
std::string runtime_path(RuntimePath path, const std::string& muhome="");
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/19053351/how-do-i-use-a-custom-deleter-with-a-stdunique-ptr-member
|
// https://stackoverflow.com/questions/19053351/how-do-i-use-a-custom-deleter-with-a-stdunique-ptr-member
|
||||||
template <auto fn>
|
template <auto fn>
|
||||||
struct deleter_from_fn {
|
struct deleter_from_fn {
|
||||||
@ -436,7 +461,7 @@ to_second(const P& p, typename P::value_type::first_type f)
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert string view in something printable with %*s
|
* Convert string view in something printable with %.*s
|
||||||
*/
|
*/
|
||||||
#define STR_V(sv__) static_cast<int>((sv__).size()), (sv__).data()
|
#define STR_V(sv__) static_cast<int>((sv__).size()), (sv__).data()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user