C++模板元编程 - 是否可以输出生成的代码?

28

我希望能够调试一些模板代码,以更好地理解它。
不幸的是,我对模板元编程比较陌生,很难入手。

当我尝试输出预处理后的源文件时,会得到 125,000 行代码 :/

那么有没有办法可以看到生成的代码呢?(我使用的库是SeqAn


2
不是,但应该是。有人应该将其作为Clang项目来完成:D - Joseph Garvin
请注意,由于SFINAE的存在,仅仅通过将每个模板替换为某些硬编码的替代方案所得到的代码可能是非法的。例如,只要从未调用,模板类的方法可能会调用不存在的内容。 - MvG
1
@JosephGarvin 现在有一个基于Clang的项目可以实现这个功能。最新的活跃版本包括Templight(基于Clang的模板实例化分析器和调试器)、Templar可视化工具以及Metashell。 - Mikael Persson
5个回答

26

一般来说,不能这样做。模板只是C++语言的一部分,它们不是单独的预处理器,因此它们不会生成C++代码。

通常的解决方案是在代码中加入静态断言和其他测试来验证正确的模板以正确的方式被实例化。

一旦你开始迷失在元编程中,这个简单的技巧可以帮助你确定模板参数的真正类型:

// given a variable t of an unknown type T
int*** i = t;

当编译器遇到这种情况时,它将会打印出一个简单易懂的错误信息:"无法将 转换为 int***",从而帮助您轻松验证模板参数 T 是否为您想要的类型。请注意保留 HTML 标签,不做解释。

24
不是这样的。预处理器与模板处理无关,模板处理是由编译器执行的。模板不会生成C++代码,就像函数调用一样-它们是C++语言本身的一个组成部分。

虽然这通常是正确的,但在理论上预处理器也是如此。实际上,最简单的实现方式是生成类似于C++代码的代码作为预处理器阶段的输出,但不适用于模板实例化。反过来则非常困难,但从技术上讲并非不可能。主要问题在于名称查找,编译器可以在模板实例化期间生成唯一的名称。 - MSalters
说实话,如果您实例化它,仍然可以使用调试器来显示计算出的类型。我制作了一个如何使用Visual C++的示例。我经常在我的元程序中使用它。 - ovanes

16

请查看我的有关C++模板元编程调试的出版物

从第6页开始,您可以了解其工作原理。对于具体的用途,您不需要整个工具链,可以手动完成。

我制作了一个Visual C++插件,您可以在其中设置断点等,但它更像是一个概念证明,而不是一个每天都会使用的工具。

我们一直在开发图形界面前端,显示所有实例化,并允许进行调试、分析。不幸的是,我们不能保证该工具的发布日期,因为我们只能利用有限的空闲时间进行开发。

更新:调试器和分析器在这里可用。

更新:C++Now演示


6
这可能是您问题的答案: C++ 模板预处理器工具 似乎已经满足了上一个提问者的需求,尽管我无法想象为什么!C++编译器在C中的输出通常非常难以阅读,因为它并不旨在帮助理解,而只是一种便携式汇编语言。

1
通常情况下,无法输出整个代码。但我发现非常有趣的是,可以使用Visual C++调试器来显示类型。以这个简单的元程序为例:
template<class Head, class Tail>
struct type_list
{
  typedef Head head;
  typedef Tail tail;
};

struct null_type
{};

template<class List>
struct list_head
{
  typedef typename List::head head;
};

template<class List>
struct list_tail
{
  typedef typename List::tail tail;
};

template<class List>
struct list_length
{
  static const size_t length = 1+list_length< typename list_tail<List>::tail >::length;
};

template<>
struct list_length<null_type>
{
  static const size_t length = 0;
};


int main()
{
  typedef 
    type_list
    < int
    , type_list
      < double
      , type_list
        < char
        , null_type
        >
      >
    >       my_types;

  my_types test1;

  size_t length=list_length<my_types>::length;

  list_head<list_tail<list_tail<my_types>::tail>::tail>::head test2;

}

我刚刚实例化了我的元类型。这些仍然是空的C++类实例,长度至少为1字节。现在,我可以在最后一个test2实例化之后设置断点,看看test1和test2的类型/值长度:

调试器显示如下:

length  3   unsigned int
test1   {...}   type_list<int,type_list<double,type_list<char,null_type> > >
test2   -52 'Ì' char

现在你知道头部返回了一个字符,你的列表包含int、double、char类型,并以null_type结尾。

这对我帮助很大。有时候你需要把混乱的类型复制到文本编辑器并将其格式化成可读的形式,但这给了你追踪内部计算内容的可能性。

希望这能帮到你,
Ovanes


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接