2

我有一个看起来大致像这样的数据结构

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

1 回答 1

4

看来唯一的选择是在代码中创建一个辅助类型,该类型执行递归模板扩展以逐个剥离模板参数。而且您必须强制编译器实例化模板,以便 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>

它最终看起来像这样: 在此处输入图像描述 在此处输入图像描述

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

  • 使用索引列表项。如果无法索引模板参数,则无法工作$T$i
  • 使用自定义列表项。同样,如果不能索引模板参数,它就无法工作。
  • 在 TableAccessor 中使用变量模板来获取类型和索引。显然 natvis 无法访问变量模板。

将来,我会直接使用 natvis DLL,而不是纠结于严格受限的 XML。

于 2019-09-19T17:01:05.513 回答