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

时间:2019-09-18 18:44:12

标签: visual-studio visual-studio-debugging natvis

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

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>

1 个答案:

答案 0 :(得分:1)

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

我开始使用的数据

struct ColumnStorage
{
    void* data;
};

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

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

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

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

它最终看起来像这样: enter image description here enter image description here

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

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

将来,我将直接使用natvis DLL,而不是大惊小怪地使用XML。