I have been working on trying to convert some legacy software to use variadic templates. However, I have been really struggling on overhauling one of the classes, which provides logging, and keeping the changes super minimal.
For example:
#define ThisFile __FILE__ << ":" << __LINE__
#include "Log.hpp"
auto main() -> int
{
auto Logger = Log{};
const auto a = int{0};
const auto b = int{1};
Logger->print("%s:%d %d %d", ThisFile, a, b);
}
I have been trying really hard to keep that syntax identical, since this logger is used in hundreds of files and in hundreds of lines of code. I have been able to get std::source_location and the variadic templates to play nicely with each other using deduction guides.
#pragma once
#include
#include
#include
#include
struct Log
{
template
print(const char* format, Args&&...args, const std::source_location& loc)
{
const auto args_pack = std::tuple{ std::forward(args)...};
const auto msg = std::apply([&format](auto&...args) {
return std::vformat(format, std::make_format_args(args...));
}, args_pack);
std::println("{}:{} {}", loc.file_name(), loc.line(), msg);
}
template
print(const std::string_view, Args&&...) -> printIt;
};
However, using this would require the syntax to be changed to something like Log::print Which would break a lot of legacy code. So I decided to wrap it in a function:
#pragma once
#include
#include
#include
#include
struct Log
{
template
printIt(const char* format, Args&&...args, const std::source_location& loc)
{
const auto args_pack = std::tuple{ std::forward(args)...};
const auto msg = std::apply([&format](auto&...args) {
return std::vformat(format, std::make_format_args(args...));
}, args_pack);
std::println("{}:{} {}", loc.file_name(), loc.line(), msg);
}
template
printIt(const std::string_view, Args&&...) -> printIt;
template
static auto print(const std::string_view format, Args&...args,const std::source_location loc = std::source_location::current()) -> void
{
printIt(format, std::forward(args)..., loc);
}
};
However, the issue with this is that I need to specify the template parameters where I am calling it:
#include "Log.hpp"
auto main() -> int
{
auto Logger = Log{};
const auto a = int{0};
const auto b = int{1};
Logger->print("{} {}", a, b);
}
Yes, I do realize that in this example I have removed %d and replaced them with {}. I have written code that will convert the C style syntax and format it into a string using snprintf. I also removed ThisFile since that will be replaced with std::source_location. What I want to achieve is this:
#include "Log.hpp"
auto main() -> int
{
auto Logger = Log{};
const auto a = int{0};
const auto b = int{1};
Logger->print("%d %d", a, b);
}
However, if I do that I do get a compiler error that says something like template argument deduction/substitution failed
Is there no way to achieve what I am trying to do without specifying the types that go into the template parameter? Or can I help the compiler by giving it hints as to what it should deduce to? I know that deduction guides cannot work on functions, or I would have tried to do that first, but I am stuck here and unsure of where to go next.