将 natvis 与模板参数包一起使用

Using natvis with template parameter packs

我的数据结构大致如下

struct Column
{
    void* data;
};

template <class... T>
struct Table
{
    size_t count;
    std::vector<Column> columns; // columns.size() == sizeof...(T)
};

并且我正在尝试通过以下方式将其可视化

+ Table
  + Column 0
      item 1
      item 2
      ...
  + Column 1
      item 1
      item 2
      ...

这是我目前拥有的:

<Type Name="Table&lt;*&gt;">
    <Expand>
        <Synthetic Name="Column 0">
            <Expand>
                <ArrayItems>
                    <Size>count</Size>
                    <ValuePointer>($T1*) columns[0].data</ValuePointer>
                </ArrayItems>
            </Expand>
        </Synthetic>

        <Synthetic Name="Column 1" Condition="columns.size() > 1">
            <Expand>
                <ArrayItems>
                    <Size>count</Size>
                    <ValuePointer>($T2*) columns[1].data</ValuePointer>
                </ArrayItems>
            </Expand>
        </Synthetic>
    </Expand>
</Type>

显然,这种扩展性非常差。我只能复制粘贴每一列的代码并添加 Condition 来启用或禁用它。我最终会得到支持的列的最大数量,之后可视化将停止显示列。

有什么方法可以更智能地显示它吗?如果我可以使用 $T$i.

之类的表达式对模板参数进行索引,我可以想象有几种方法可以做到这一点

我真正想做的是这样的:

<Type Name="Table&lt;*&gt;">
    <Expand>
        <ArrayItems>
            <Size>columns.size()</Size>
            <Value>
                <Synthetic Name="Column %i">
                <Expand>
                    <ArrayItems>
                        <Size>count</Size>
                        <ValuePointer>($T$i2*) columns[$i2].data</ValuePointer>
                    </ArrayItems>
                </Expand>
            </Synthetic>
            </Value>
        </ArrayItems>
    </Expand>
</Type>

看来唯一的选择是在代码中做一个辅助类型,进行递归模板扩展,将模板参数一一剥离。 并且您必须强制编译器实例化模板,以便 natvis 可以使用它。

我开始的数据

struct ColumnStorage
{
    void* data;
};

struct TableStorage
{
    size_t rowCount;
    std::vector<ColumnStorage> columns;
};

template <class Table, class... TableColumns>
struct TableAccessor
{
    TableStorage* tableStorage;
};

这就是我需要添加的以获得体面的 natvis

// The helper type that allows natvis to work. The first template parameter keeps track of the
// column index so we know where to index into TableStorage::columns. The second parameter isn't
// used here. The third parameter is the concrete types of each column.
template <int i, class Table, class... TableColumns>
struct NatvisColumnView;

template <class Table, class... TableColumns>
struct TableAccessor
{
    TableStorage* tableStorage;

    // Used by natvis to cast `this`
    using NatvisView = NatvisColumnView<0, Table, TableColumns...>;

    // Force the compiler to instantiate the template or it won't be available to natvis
    TableAccessor() { (NatvisView*) this; }
};

// End the template recursion. Inherit from TableAccessor so that tableStorage can be used
template <int i, class Table, class Column>
struct NatvisColumnView<i, Table, Column> : TableAccessor<Table, Column> {};

// Recursive template to peel off column types one-by-one
template <int i, class Table, class FirstColumn, class... RestColumns>
struct NatvisColumnView<i, Table, FirstColumn, RestColumns...> : NatvisColumnView<i + 1, Table, RestColumns...>
{
    using base = typename NatvisColumnView<i + 1, Table, RestColumns...>;
};
<Type Name="TableAccessor&lt;*,*&gt;">
    <DisplayString>Table</DisplayString>
    <Expand>
    <Item Name="Count">tableStorage->rowCount</Item>
    <!-- Cast `this` to the view type and use the for visualization -->
    <ExpandedItem>*(NatvisView*) this</ExpandedItem>
    </Expand>
</Type>

<!-- Bottom out the recursive view -->
<Type Name="NatvisColumnView&lt;*,*,*&gt;">
    <DisplayString>NatvisColumnView</DisplayString>
    <Expand>
    <Synthetic Name="Column">
        <Expand>
        <ArrayItems>
            <Size>tableStorage->rowCount</Size>
            <ValuePointer>($T3*) tableStorage->columns[$T1].data</ValuePointer>
        </ArrayItems>
        </Expand>
    </Synthetic>
    </Expand>
</Type>

<!-- Display the first column then recurse -->
<Type Name="NatvisColumnView&lt;*,*,*,*&gt;">
    <DisplayString>NatvisColumnView</DisplayString>
    <Expand>
    <Synthetic Name="Column">
        <Expand>
        <ArrayItems>
            <Size>tableStorage->rowCount</Size>
            <!-- Show the correct column using the column index (first template parameter)
                 and the column type (third template parameter) -->
            <ValuePointer>($T3*) tableStorage->columns[$T1].data</ValuePointer>
        </ArrayItems>
        </Expand>
    </Synthetic>
    <ExpandedItem>*(base*) this</ExpandedItem>
    </Expand>
</Type>

最后看起来像这样:

我尝试了其他各种方法,例如:

  • 使用 IndexListItems。如果无法索引模板参数 $T$i.
  • 则无法工作
  • 使用 CustomListItems。同样,如果不能索引模板参数则无法工作。
  • 在 TableAccessor 中使用变量模板获取类型和索引。显然 natvis 无法访问变量模板。

将来,我会直接使用 natvis DLL,而不是为严重受限的 XML。