如何在可变参数模板函数中使用source_location?

How to use source_location in a variadic template function?

本文关键字:source location 函数 变参 参数      更新时间:2023-10-16

C++20 功能std::source_location用于捕获有关调用函数的上下文的信息。 当我尝试将其与可变参数模板函数一起使用时,我遇到了一个问题:我看不到放置source_location参数的位置。

以下内容不起作用,因为可变参数必须位于末尾:

// doesn't work
template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());

以下内容也不起作用,因为调用方将入的参数搞砸:

// 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

我在评论中被告知std::source_location可以与可变参数模板无缝协作,但我很难弄清楚如何。 如何将std::source_location与可变参数模板函数一起使用?

第一种形式可以通过添加演绎指南来工作:

template <typename... Ts>
struct debug
{    
debug(Ts&&... ts, const std::source_location& loc = std::source_location::current());
};
template <typename... Ts>
debug(Ts&&...) -> debug<Ts...>;

测试:

int main()
{
debug(5, 'A', 3.14f, "foo");
}

演示

如果您的函数在可变参数之前有一个固定参数,例如 printf 格式字符串,则可以将该参数包装在其构造函数中捕获source_location的结构中:

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 %sn", "world"); }

只需将您的参数放在元组中,无需宏。

#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';
}

这有效*

从技术上讲,您可以只写:

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';
}

但是,您可能需要跳过一些箍才能获得参数类型。


* 在链接到的示例中,我使用的是<experimental/source_location>,因为这是编译器现在接受的。此外,我还添加了一些用于打印参数元组的代码。

template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());

"works",但需要指定模板参数,因为没有最后一个,因此不可推导:

debug<int>(42);

演示

可能的(不完美(替代方案包括:

  • 使用具有硬编码限制的重载("处理"可变变量的旧可能方法(:

    // 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());
    // ...
    

    演示

  • source_location放在第一个位置,而不默认:

    template <typename... Args>
    void debug(const std::source_location& loc, Args&&... args);
    

    debug(std::source_location::current(), 42);
    

    演示

  • 类似于重载,但只需使用元组作为组

    template <typename Tuple>
    void debug(Tuple&& t,
    const std::source_location& loc = std::source_location::current());
    

    template <typename ... Ts>
    void debug(const std::tuple<Ts...>& t,
    const std::source_location& loc = std::source_location::current());
    

    用法

    debug(std::make_tuple(42));
    

    演示

不是一个很好的解决方案,但是...将可变参数放在std::tuple中怎么样?

我的意思是。。。作为

template <typename... Args>
void debug (std::tuple<Args...> && t_args,
std::source_location const & loc = std::source_location::current());

不幸的是,这样你必须显式调用std::make_tuple调用它

debug(std::make_tuple(1, 2l, 3ll));

你可以尝试制作它:

#include <iostream>
#include <experimental/source_location>
struct log
{
log(std::experimental::source_location location = std::experimental::source_location::current()) : location { location } {}
template<typename... Args>
void operator() (Args... args)
{
std::cout << location.function_name() << std::endl;
std::cout << location.line() << std::endl;
}
std::experimental::source_location location;
};
int main() 
{
log()("asdf");
log()(1);
}

演示

如果你可以接受宏的使用,你可以写这个来避免显式传入std::source_ location::current()

template <typename... Args>
void debug(const std::source_location& loc, Args&&... args);
#define debug(...) debug(std::source_location::current() __VA_OPT__(,) __VA_ARGS__)