Given the following code:
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <source_location>
template<std::size_t N>
struct StringWrapper {
// consteval to force only compile-time strings
consteval StringWrapper(const char (&format_string)[N], std::source_location source_location = std::source_location::current())
: SourceInfo{ source_location } {
std::copy(std::begin(format_string), std::end(format_string), std::begin(FormatString));
}
char FormatString[N]{};
// This here to motivate not using StringWrapper as non-type template argument
std::source_location SourceInfo{};
};
// class-template-argument-deduction is not necessary here, see the first case in main
// variadic template, thus putting std::source_location at the end is not an option
template<std::size_t N, class... Args>
void log(StringWrapper<N> format_string, Args&&... args);
int main()
{
log(StringWrapper{ "This {}, explicitly instantiating, but deducing the parameter" }, "works");
log("This {}, could not match 'StringWrapper<N>' against 'const char *'", "fails");
}
What could be a possible workaround to this issue that would not require a change on the callsite?
One possibility is to use a std::string_view
, however that would not give me access to N
as a constant expression in the implementation of log
and is thus not an option in my case.
Another possibility is to directly take a const char (&)[N]
, however then I am losing the std::source_location
which is not an option either.
I understand why overload resolution would favor a function that takes a const char*
over a function that takes a const char (&)[N]
, however I do not understand why 'StringWrapper' is not even attempted to match against 'const char (&)[N]' however.
CodePudding user response:
you can use class template argument deduction to do this (change log
to object constructor instead of normal function)
template<std::size_t N, class... Args>
struct log{
log(StringWrapper<N> src, Args&&... args){}
};
template<std::size_t N, class... Args>
log(const char(&)[N],Args&&...) -> log<N,Args...>;
template<std::size_t N, class... Args>
log(StringWrapper<N>,Args&&...) -> log<N,Args...>;
CodePudding user response:
Although you said you somehow need N
inside log
as constant expression, just in case someone reach here don't need it.
Change StringWrapper
to non-template can also solve the problem.
struct StringWrapper {
template<std::size_t N>
consteval StringWrapper(const char(&format_string)[N], std::source_location source_location = std::source_location::current())
: FormatString{format_string}, SourceInfo{ source_location }{}
std::string_view FormatString;
std::source_location SourceInfo{};
};
template<typename ... Args>
void log(StringWrapper src, Args&&...){
auto N = src.FormatString.size(); // N, but not constant expression
}