因此,我实现了一个相当琐碎的记录器,但我想对其进行扩展,以便我可以将数据参数传递给它,可能是通过格式化,而我似乎无法弄清楚如何做到最好。
到目前为止,它是这样写的:
// Standard Headers.
#include <ostream>
#include <variant>
#include <memory>
#include <utility>
#include <mutex>
#include <array>
#include <string_view>
#include <iostream>
namespace Logger {
// Various logging severity levels.
enum class Level {
Info
};
class Log {
public:
// Takes standard streams cout, cerr, etc.
explicit Log(std::ostream& p_stream) : m_log(&p_stream) {}
// Create logger using std::make_unique<std::ofstream>(...) so ownership is passed.
explicit Log(std::unique_ptr<std::ostream> p_stream) : m_log(std::move(p_stream)) {}
template <typename T>
inline void info(T&& p_message);
private:
template <typename T>
void log(T&& p_msg) const {
auto const t_lock = std::lock_guard(*m_lock);
std::visit([&](auto&& p_ptr) {
(*p_ptr) << p_msg;
}, m_log);
};
std::ostream& stream() const {
return std::visit([](auto&& ptr) -> std::ostream& {
return *ptr;
}, m_log);
}
template <typename T>
inline void add(Logger::Level p_level, T&& p_message);
std::variant<std::unique_ptr<std::ostream>, std::ostream*> m_log;
std::unique_ptr<std::mutex> m_lock = std::make_unique<std::mutex>();
std::array<std::string_view, 1> m_levels = { "Info" };
};
template <typename T>
void Log::add(Level p_level, T&& p_message) {
auto const f_lock = std::lock_guard(*m_lock);
stream() << m_levels[static_cast<size_t>(p_level)] << ": " << p_message << '\n';
}
template <typename T>
inline void Log::info(T&& p_message) {
add(Level::Info, p_message);
}
}
int main() {
auto logger = Logger::Log(std::cout);
logger.info("Hello, world!");
return 0;
}
我想做的是使用.info()
时能够指定写入日志时将被替换的任意数量的参数,类似于:
logger.info("Some error message with arg: {}", 1);
我将如何去做?最好的方法是什么?
答案 0 :(得分:0)
出于某些奇怪的原因,我以这种方式做类似的事情(您将不得不根据自己的需要进行调整):
inline
std::ostream &
txt(std::ostream &output,
const char *format)
{
return output << format;
}
template<typename First,
typename ...Args>
inline
std::ostream &
txt(std::ostream &output,
const char *format,
First &&first,
Args &&...args)
{
while(*format)
{
if(*format=='%')
{
return txt(output << std::forward<First>(first),
++format, std::forward<Args>(args)...);
}
output << *format++;
}
return output;
}
前面的两个函数在格式字符串中使用标记%
来插入下一个参数(无论是什么,它是一个值,一个格式说明符,一个自定义对象...)在流中。
当然,某些适配器可以简化用法:
template<typename ...Args>
inline
std::string
txt(const char *format,
Args &&...args)
{
std::ostringstream output;
txt(output, format, std::forward<Args>(args)...);
return output.str();
}
例如:
std::ostream &
operator<<(std::ostream &output,
const MyStuff &ms)
{
return output << '[' << ms.member1 << '|' << ms.member2 << ']';
}
...
MyStuff my_stuff= ... ;
auto msg=txt("an integer %, a (formatted) real %% and something else %\n",
12, std::setprecision(12), 34.56, my_stuff);
然后应该可以调整Log::add()
(然后是Log::info()
和任何相关内容),使其以与问题中预期的类似的方式可用
logger.info("Some error message with arg: %", 1);
希望有帮助。
答案 1 :(得分:0)
我通常使用std::any。这是一个非优化的版本(使用副本而不是引用,从向量中删除项目等),但是基本思想是使用此行将编译时参数包转换为运行时参数包:
std::vector<std::any> a = {args ...};
您可以将引用与std::vector<std::any> a = {std::ref(args)...};
完整功能如下:
template<typename ... many>
void safe_printf2(const char *s, many ... args)
{
using namespace std;
vector<any> a = {args ...};
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
if (a.empty())
throw logic_error("Fewer arguments provided to printf");
if (a[0].type() == typeid(string)) cout << any_cast<string>(a[0]);
if (a[0].type() == typeid(int)) cout << any_cast<int>(a[0]);
if (a[0].type() == typeid(double)) cout << any_cast<double>(a[0]);
a.erase(a.begin());
s++;
}
}
cout << *s++;
}
}
示例:
safe_printf2("Hello % how are you today? I have % eggs and your height is %","Jack"s, 32,5.7);