I'm trying to make a short-hand for logging (log4cplus) + libfmt. I have defined a macro in my util header:
#define T_TRACE(fs, ...) \
LOG4CPLUS_TRACE(Logger::Instance().logger, fmt::format(fs, __VA_ARGS__).c_str())
Then, every other cpp just include this header, invoke T_TRACE
to perform logging. e.g.
T_TRACE("Version is {}", "1.0.0");
Everything works fine until I want to log a string contains {}
pair (a simple json string).
T_TRACE("{\"name\": \"value\"}")
, the fmt
lib will treat it as a formatter, so I need to inform the macro that insert a default placeholder "{}"
for me if there is only one argument.
The reason for using macro is that I also want to log the source file and lines. If I wrap it in a function, all the file/line info would be in that wrapper function.
Finally, I come up with a solution based on Kramer's answer.
Unamed logging:
T_TRACE(config) => FMT_SERIALIZER_RESULT
#define T_TRACE(o, ...) \
LOG4CPLUS_TRACE(Logger::Instance().logger, LogImpl(o, __VA_ARGS__).c_str())
template <typename T, typename... args_t>
inline auto LogImpl(const T& obj, args_t&&... args)
{
if constexpr (sizeof...(args_t))
return fmt::format(obj, args...);
else
return fmt::format("{}", obj);
}
Named logging:
T_TRACE(config) => config: FMT_SERIALIZER_RESULT
#define T_TRACE(o, ...) \
LOG4CPLUS_TRACE(Logger::Instance().logger, LogImpl(#o, o, __VA_ARGS__).c_str())
template <typename T, typename... args_t>
inline auto LogImpl(const char* name, const T& obj, args_t&&... args)
{
if constexpr (sizeof...(args_t))
return fmt::format(obj, args...);
else
return fmt::format("{}: {}", name, obj);
}