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

L. *_* F. 33 c++ default-arguments variadic-templates c++20 std-source-location

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());
Run Code Online (Sandbox Code Playgroud)

以下内容也不起作用,因为调用者将被介于两者之间的参数所困扰:

// 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
Run Code Online (Sandbox Code Playgroud)

可以在可变参数模板中无缝使用的评论中告诉我std::source_location,但是我很难弄清楚该如何做。如何std::source_location与可变参数模板函数一起使用?

Pio*_*cki 30

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

template <typename... Ts>
debug(Ts&&...) -> debug<Ts...>;
Run Code Online (Sandbox Code Playgroud)

测试:

int main()
{
    debug(5, 'A', 3.14f, "foo");
}
Run Code Online (Sandbox Code Playgroud)

演示


ein*_*ica 7

只需将您的参数放在一个元组中,就不需要宏。

#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';
}
Run Code Online (Sandbox Code Playgroud)

有效*

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

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';
}
Run Code Online (Sandbox Code Playgroud)

但是然后您可能不得不跳过一些障碍才能获得参数类型。


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

  • “ *这很好用*”,您的意思是,除了必须将值放入元组之外?因此,必须处理大量毫无意义的语法才能真正提取它们并将其用于预期目的吗? (3认同)

小智 7

如果您的函数在可变参数之前有一个固定参数,例如 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 %s\n", "world"); }
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个解决方案(与推导指南中接受的答案相比):1.)如果需要,它允许您手动传递 source_location 2.)该函数仍然是一个函数(并且不会成为结构/构造函数调用),这允许您添加 [[ noreturn ]] --&gt; 如果这应该记录致命错误,则很有用 (2认同)

Jar*_*d42 5

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

“有效”,但需要指定模板参数,因为存在不可推导的参数,因为没有最后一个:

debug<int>(42);
Run Code Online (Sandbox Code Playgroud)

演示

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