Problem Code:
#include <filesystem>
#include <iosfwd>
#include <string>
#include <format>
#include <source_location>
#include <iostream>
#include <array>
#include <wchar.h>
#include <fstream>
#include <memory>
#include <exception>
using std::filesystem::path;
using std::array;
using std::source_location;
using std::format;
using std::vformat;
using std::wstring;
class CStreamToTextFileU {
std::unique_ptr<std::wofstream> m_pFileStr;
CStreamToTextFileU() = delete;
public:
explicit CStreamToTextFileU(const std::filesystem::path& Filepath, std::ios::openmode nMode) :
m_pFileStr(std::make_unique<std::wofstream>(Filepath, nMode | std::ios::out)) {
if((m_pFileStr.get() == nullptr) || (! m_pFileStr->is_open()))
throw std::ios::failure("Unable to open file for writing");
}
CStreamToTextFileU(CStreamToTextFileU&& Other) noexcept : m_pFileStr(std::move(Other.m_pFileStr)) {}
CStreamToTextFileU &operator=(CStreamToTextFileU&& Other) & noexcept {
if(this != &Other)
m_pFileStr = std::move(Other.m_pFileStr);
return(*this);
}
~CStreamToTextFileU() noexcept {
if(m_pFileStr != nullptr)
m_pFileStr->close();
}
std::wofstream& operator()() const { return(*(m_pFileStr.get())); }
};
class RLog {
CStreamToTextFileU LogStream;
public:
RLog() = delete;
explicit RLog(const path Filepath);
template<auto Separator = L' ', typename... Args>
void LogMsg(Args&&... args, const std::source_location Cur = std::source_location::current());
}; // class RLog()
// template<typename... Args> LogMsg(Args&&...) -> LogMsg<Args...>;
RLog::RLog(const path Filepath) : LogStream(CStreamToTextFileU(Filepath, std::ios::trunc)) {
LogStream() << L"Log file: " << Filepath << L"\n\n";
} // RLog::RLog()
template<auto Separator, typename... Args>
void RLog::LogMsg(Args&&... args, const std::source_location Cur) {
static int n{};
RLog::LogStream() << format(L"{:04} File: {}({}:{}) `{}`:",
n++, Cur.file_name(), Cur.line(),
Cur.column(), Cur.function_name());
(... , [] (const auto&& arg) -> const auto { RLog::LogStream() << Separator
<< std::forward<Args>(arg); } (args));
RLog::LogStream() << L'\n';
} // RLog::LogMsg()
int main() {
path LogPath {L"RLogFile.txt"};
RLog Lg {LogPath};
Lg.LogMsg(L"This is an", L"error", L'\n');
} // main()
I'm compiling with VS, latest. The quoted code will run in Compiler Explorer with the latest x86 VS compiler, and the flag /std:c++latest. The errors I get are:
example.cpp
<source>(70): error C2672: 'RLog::LogMsg': no matching overloaded function found
<source>(70): error C2780: 'void RLog::LogMsg(Args &&...,const std::source_location)': expects 2 arguments - 3 provided
<source>(48): note: see declaration of 'RLog::LogMsg'
Compiler returned: 2
I've tried a number of ways to get around the problem, including different parameter orders, embedding LogMsg() in a nested struct, making the source_location argument a template parameter (can't deduce from a default), et cetera. The commented-out deduction guide would fix it, but it won't work, because the problem function is a member, and not a constructor, so the compiler just sees it as a broken function declaration.
The idea of the code should be obvious: to call the class constructor in a scope that encloses everywhere you want to log, then call the LogMsg() function to create the log messages. I'd rather not go away from the scheme. I know I could fix things by opening and closing the file stream for each message, because then the whole thing could be in the class constructor, but that seems inelegant, and I'd rather get this to work.