57

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可变参数模板函数?

4

5 回答 5

58

通过添加扣除指南,可以使第一种形式起作用:

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");
}

演示

于 2019-08-18T21:24:20.983 回答
9

如果您的函数在可变参数之前有一个固定参数,例如 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"); }
于 2021-02-27T18:51:53.667 回答
8

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

#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>的是因为这是编译器现在接受的。另外,我添加了一些代码来打印参数元组。

于 2019-08-18T19:40:06.717 回答
4
template <typename... Args>
void debug(Args&&... args,
           const std::source_location& loc = std::source_location::current());

“有效”,但需要指定模板参数,因为没有最后一个:

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));
    

    演示

于 2019-08-18T19:51:43.287 回答
3

不是一个很好的解决方案,但是......如何将可变参数放在 a 中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));
于 2019-08-18T18:54:18.970 回答