将 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<*>">
<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<*>">
<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<*,*>">
<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<*,*,*>">
<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<*,*,*,*>">
<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。
我的数据结构大致如下
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<*>">
<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<*>">
<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<*,*>">
<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<*,*,*>">
<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<*,*,*,*>">
<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。