The C++20 feature std::source_location
is used to capture information about the context in which a function is called.
When I try to use it with a variadic template function, I encountered a problem: I can't see a place to put the source_location
parameter.
The following doesn't work because variadic parameters have to be at the end:
// doesn't work
template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());
The following doesn't work either because the caller will be screwed up by the parameter inserted in between:
// doesn't work either, because ...
template <typename... Args>
void debug(const std::source_location& loc = std::source_location::current(),
Args&&... args);
// the caller will get confused
debug(42); // error: cannot convert 42 to std::source_location
I was informed in a comment that std::source_location
works seamlessly with variadic templates, but I struggle to figure out how. How can I use std::source_location
with variadic template functions?
The first form can be made to work, by adding a deduction guide:
template <typename... Ts>
struct debug
{
debug(Ts&&... ts, const std::source_location& loc = std::source_location::current());
};
template <typename... Ts>
debug(Ts&&...) -> debug<Ts...>;
Test:
int main()
{
debug(5, 'A', 3.14f, "foo");
}
DEMO
If your function has a fixed parameter before the variadiac arguments, like a printf format string, you could wrap that parameter in a struct that captures source_location in its constructor:
struct FormatWithLocation {
const char* value;
std::source_location loc;
FormatWithLocation(const char* s,
const std::source_location& l = std::source_location::current())
: value(s), loc(l) {}
};
template <typename... Args>
void debug(FormatWithLocation fmt, Args&&... args) {
printf("%s:%d] ", fmt.loc.file_name(), fmt.loc.line());
printf(fmt.value, args...);
}
int main() { debug("hello %s\n", "world"); }
Just put your arguments in a tuple, no macro needed.
#include <source_location>
#include <tuple>
template <typename... Args>
void debug(
std::tuple<Args...> args,
const std::source_location& loc = std::source_location::current())
{
std::cout
<< "debug() called from source location "
<< loc.file_name() << ":" << loc.line() << '\n';
}
And this works*.
Technically you could just write:
template <typename T>
void debug(
T arg,
const std::source_location& loc = std::source_location::current())
{
std::cout
<< "debug() called from source location "
<< loc.file_name() << ":" << loc.line() << '\n';
}
but then you'd probably have to jump through some hoops to get the argument types.
* In the linked-to example, I'm using <experimental/source_location>
because that's what compilers accept right now. Also, I added some code for printing the argument tuple.
template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());
"works", but requires to specify template arguments as there are non deducible as there are not last:
debug<int>(42);
Demo
Possible (not perfect) alternatives include:
use overloads with hard coded limit (old possible way to "handle" variadic):
// 0 arguments
void debug(const std::source_location& loc = std::source_location::current());
// 1 argument
template <typename T0>
void debug(T0&& t0,
const std::source_location& loc = std::source_location::current());
// 2 arguments
template <typename T0, typename T1>
void debug(T0&& t0, T1&& t1,
const std::source_location& loc = std::source_location::current());
// ...
Demo
to put source_location
at first position, without default:
template <typename... Args>
void debug(const std::source_location& loc, Args&&... args);
and
debug(std::source_location::current(), 42);
Demo
similarly to overloads, but just use tuple as group
template <typename Tuple>
void debug(Tuple&& t,
const std::source_location& loc = std::source_location::current());
or
template <typename ... Ts>
void debug(const std::tuple<Ts...>& t,
const std::source_location& loc = std::source_location::current());
with usage
debug(std::make_tuple(42));
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With