Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using {fmt} & source_location to create variadic-template-based logging function

I'd like to create a simple log function in C++ which prepend the code location to the log message. I'd like to avoid macros overall as well as the usage of __FILE__ & __LINE__.

Note that the format string is always a compile-time string, and I'd like to have a much computation as possible on compile time (the target machine is a small MCU).

I can use the C++20 source_location feature via experimental/source_location. I can also use {fmt}.

I started off from this. Currently, I've got the following:

#include <fmt/format.h>
#include <experimental/source_location>

using source_location = std::experimental::source_location;

void vlog(fmt::string_view format, fmt::format_args args)
{
  fmt::vprint(format, args);
}

template <typename S, typename... Args>
void log(const S& format, const source_location& location, Args&&... args)
{
  vlog(format, fmt::make_args_checked<fmt::string_view, uint32_t, Args...>(format, location.file_name(), location.line(), args...));
}

#define MY_LOG(format, ...) log(FMT_STRING("{},{}, " format), source_location::current(), __VA_ARGS__)

int main() {
  MY_LOG("invalid squishiness: {}", 42);
}

Which yields correctly ./example.cpp,20, invalid squishiness: 42.

Looks to me like I'm pretty close. I think what's left is to make the log function take a default argument for source_location (I understand that source_location::current() as a default arguement is a good practice). I'm getting the following error though:

:12:99: error: missing default argument on parameter 'args'

Is this even possible to mix variadic templates and default arguments for parameters? If so, how?

Also, is there a way to prepend the "{},{}, " part to the compile-time format string to yield yet another compile-time string (to be used as format)?

like image 527
galah92 Avatar asked Feb 25 '21 21:02

galah92


People also ask

What is FMT C++?

{fmt} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams.

What is FMT in coding?

fmt stands for the Format package. This package allows to format basic strings, values, or anything and print them or collect user input from the console, or write into a file using a writer or even print customized fancy error messages. This package is all about formatting input and output.


Video Answer


1 Answers

You can do it with a struct that represents the format string and location:

#include <fmt/core.h>
#include <source_location>

struct format_string {
  fmt::string_view str;
  std::source_location loc;

  format_string(
      const char* str,
      const std::source_location& loc =
        std::source_location::current()) : str(str), loc(loc) {}
};

void vlog(const format_string& format, fmt::format_args args) {
  const auto& loc = format.loc;
  fmt::print("{}:{}: ", loc.file_name(), loc.line());
  fmt::vprint(format.str, args);
}

template <typename... Args>
void log(const format_string& format, Args&&... args) {
  vlog(format, fmt::make_format_args(args...));
}

int main() {
  log("invalid squishiness: {}", 42);
}

This prints:

./example.cpp:26: invalid squishiness: 42

Godbolt: https://godbolt.org/z/4aMKcW

like image 91
vitaut Avatar answered Oct 05 '22 20:10

vitaut