I am writing a logging class, which uses source location as a defaulted argument.
I'd like to have a printf-like function call which I hope should be possible using CTAD, but in this case it is for a templated function rather than a templated class, so clearly vlog
is not a type (it's a function) hence the error.
Not quite sure where I am going wrong or whether this is actually possible??
log
takes a basic string view and works fine.
vlog
is the "clever" bit that takes a printf style argument list, i.e. a const char*
then variadic arguments of different types. I've tried to include as much code as possible in case there might be a better way of doing this!
C++17 and thanks in advance.
#include <chrono>
#include <experimental/source_location>
#include <mutex>
#include <string_view>
#include <string>
#include <iostream>
namespace LOGGING
{
class Logging
{
using Ms = std::chrono::milliseconds; ///< typename alias
using source_location = std::experimental::source_location; ///< typename alias
constexpr static Ms defalt_mutex_wait{100};
public:
static bool log(const std::string_view& msg,
const source_location& location = source_location::current())
{
if (mutx.try_lock_for(defalt_mutex_wait))
{
std::cout << location.file_name() << "[" << \
location.line() << ":" << \
location.function_name() << "]: " << \
msg;
std::cout << std::endl; // flush
mutx.unlock();
return true;
}
return false;
}
template <typename... T>
static bool vlog(const char*&& fmt, T&&... vars,
const source_location& location = source_location::current())
{
if (mutx.try_lock_for(defalt_mutex_wait))
{
char buf[100]{};
snprintf(buf, sizeof(buf), fmt, vars...);
std::cout << location.file_name() << "[" << \
location.line() << ":" << \
location.function_name() << "]: " << \
buf;
std::cout << std::endl; // flush
mutx.unlock();
return true;
}
return false;
}
template <typename... T>
vlog(const char*, T&&...) -> vlog<T...>;
template<class Rep, class Period>
[[nodiscard]] static bool lock(std::unique_lock<std::recursive_timed_mutex>& lock,
const std::chrono::duration<Rep, Period>& timeout_duration = Ms(100))
{
if (mutx.try_lock_for(timeout_duration))
{
lock = std::move(std::unique_lock<std::recursive_timed_mutex>(mutx));
mutx.unlock();
return true;
}
return false;
}
static bool lock(std::unique_lock<std::recursive_timed_mutex>& lock)
{
lock = std::move(std::unique_lock<std::recursive_timed_mutex>(mutx));
return true;
}
private:
Ms instance_mutex_wait{defalt_mutex_wait};
public:
Logging(void) noexcept = default;
private:
static std::recursive_timed_mutex mutx;
};
inline std::recursive_timed_mutex Logging::mutx{};
inline Logging LOG;
} // namespace LOGGING
int main()
{
using namespace LOGGING;
LOG.log("Hello world!");
LOG.vlog<int>("Value is %d", 42);
LOG.vlog("Value is %d", 42); // HERE
return 0;
}
<source>:58:34: error: 'vlog<T ...>' does not name a type
58 | vlog(const char*, T&&...) -> vlog<T...>;
| ^~~~~~~~~~
<source>:58:44: error: expected constructor, destructor, or type conversion before ';' token
58 | vlog(const char*, T&&...) -> vlog<T...>;
Context for anyone interested; it is for a multi-threaded and multi-core embedded system (ESP32) and the main need is to prevent different thread log messages getting interleaved.