我正在寻找一种在 VS2012 中通过 NatVis 显示 UUID 的正确方法。我自己的 uuid 类型在内部使用 UUID big-endian,因此转换为 (GUID*) 不起作用,因为 GUID 在 Windows 中使用 little-endian。所以我总是看到一个误传的uuid。
此外,Natvis 中的任何格式说明符看起来都不好看,因为在使用十六进制表示法时我无法摆脱输出中的 0x。有任何想法吗?
我正在寻找一种在 VS2012 中通过 NatVis 显示 UUID 的正确方法。我自己的 uuid 类型在内部使用 UUID big-endian,因此转换为 (GUID*) 不起作用,因为 GUID 在 Windows 中使用 little-endian。所以我总是看到一个误传的uuid。
此外,Natvis 中的任何格式说明符看起来都不好看,因为在使用十六进制表示法时我无法摆脱输出中的 0x。有任何想法吗?
这种方法远非漂亮,但它可以完成工作。
首先,您需要在代码中的某处使用一个虚拟类型来处理以十六进制显示单个字节而没有任何前缀。这感觉就像一个非常肮脏的 hack,因为我们必须在我们的代码中引入一个额外的类型,只是为了正确的调试可视化。
namespace dummy {
struct hex_dummy {
unsigned char c;
};
}
只要调试器能够在我们想要查看 uuid 的上下文中找到它,这种类型几乎可以放置在任何地方。
不幸的是,下一步几乎同样糟糕。为了能够在没有0x
前缀的情况下以十六进制打印一个字节,我们引入了一个调试可视化器hex_dummy
,用于多达 256 种不同的DisplayStrings
:
<Type Name="dummy::hex_dummy">
<DisplayString Condition="(c == 0x00)">00</DisplayString>
<DisplayString Condition="(c == 0x01)">01</DisplayString>
<DisplayString Condition="(c == 0x02)">02</DisplayString>
<DisplayString Condition="(c == 0x03)">03</DisplayString>
<DisplayString Condition="(c == 0x04)">04</DisplayString>
<DisplayString Condition="(c == 0x05)">05</DisplayString>
<DisplayString Condition="(c == 0x06)">06</DisplayString>
<DisplayString Condition="(c == 0x07)">07</DisplayString>
<DisplayString Condition="(c == 0x08)">08</DisplayString>
<DisplayString Condition="(c == 0x09)">09</DisplayString>
<DisplayString Condition="(c == 0x0a)">0A</DisplayString>
<DisplayString Condition="(c == 0x0b)">0B</DisplayString>
<DisplayString Condition="(c == 0x0c)">0C</DisplayString>
<DisplayString Condition="(c == 0x0d)">0D</DisplayString>
<DisplayString Condition="(c == 0x0e)">0E</DisplayString>
<DisplayString Condition="(c == 0x0f)">0F</DisplayString>
<DisplayString Condition="(c == 0x10)">10</DisplayString>
<DisplayString Condition="(c == 0x11)">11</DisplayString>
...
你明白了。
有了这些,可视化 uuid 就很容易了。我用来boost::uuid
测试这个:
<Type Name="boost::uuids::uuid">
<DisplayString>uuid {*(dummy::hex_dummy*)(&data[0])}{*(dummy::hex_dummy*)(&data[1])}{*(dummy::hex_dummy*)(&data[2])}{*(dummy::hex_dummy*)(&data[3])}-{*(dummy::hex_dummy*)(&data[4])}{*(dummy::hex_dummy*)(&data[5])}-{*(dummy::hex_dummy*)(&data[6])}{*(dummy::hex_dummy*)(&data[7])}-{*(dummy::hex_dummy*)(&data[8])}{*(dummy::hex_dummy*)(&data[9])}-{*(dummy::hex_dummy*)(&data[10])}{*(dummy::hex_dummy*)(&data[11])}{*(dummy::hex_dummy*)(&data[12])}{*(dummy::hex_dummy*)(&data[13])}{*(dummy::hex_dummy*)(&data[14])}{*(dummy::hex_dummy*)(&data[15])}</DisplayString>
</Type>
您可以通过使用 boost's 创建的 uuid 对其进行测试来轻松验证它是否有效uuid_generator
:
boost::uuids::uuid const test_id =
boost::uuids::string_generator()("{01234567-89AB-CDEF-0123-456789ABCDEF}");
现在这个解决方案不仅非常丑陋,而且调试器似乎需要一些时间来处理大量hex_dummy
分支,导致在调试时将鼠标悬停在 uuid 上时弹出鼠标悬停观察窗口的明显延迟。
我对这个解决方案很不满意,但到目前为止,这是我能想到的最好的解决方案。如果有人在不牺牲最终输出的清晰度的情况下看到任何改进的潜力,我会很高兴听到他们的声音。
编辑:一个小的改进 - 通过引入两种虚拟类型而不是一种,我至少可以摆脱弹出延迟。这个想法是两个使用单独的假人来打印每个字节的上半字节和下半字节,所以我们必须每个字节做两个 16 路分支,而不是一个 256 路分支。
namespace dummy {
struct hex_dummy_low {
unsigned char c;
};
struct hex_dummy_high {
unsigned char c;
};
}
代理可视化工具:
<Type Name="dummy::hex_dummy_low">
<DisplayString Condition="((c & 0x0f) == 0x00)">0</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x01)">1</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x02)">2</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x03)">3</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x04)">4</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x05)">5</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x06)">6</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x07)">7</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x08)">8</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x09)">9</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x0a)">A</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x0b)">B</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x0c)">C</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x0d)">D</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x0e)">E</DisplayString>
<DisplayString Condition="((c & 0x0f) == 0x0f)">F</DisplayString>
</Type>
<Type Name="dummy::hex_dummy_high">
<DisplayString Condition="((c >> 4) == 0x00)">0</DisplayString>
<DisplayString Condition="((c >> 4) == 0x01)">1</DisplayString>
<DisplayString Condition="((c >> 4) == 0x02)">2</DisplayString>
<DisplayString Condition="((c >> 4) == 0x03)">3</DisplayString>
<DisplayString Condition="((c >> 4) == 0x04)">4</DisplayString>
<DisplayString Condition="((c >> 4) == 0x05)">5</DisplayString>
<DisplayString Condition="((c >> 4) == 0x06)">6</DisplayString>
<DisplayString Condition="((c >> 4) == 0x07)">7</DisplayString>
<DisplayString Condition="((c >> 4) == 0x08)">8</DisplayString>
<DisplayString Condition="((c >> 4) == 0x09)">9</DisplayString>
<DisplayString Condition="((c >> 4) == 0x0a)">A</DisplayString>
<DisplayString Condition="((c >> 4) == 0x0b)">B</DisplayString>
<DisplayString Condition="((c >> 4) == 0x0c)">C</DisplayString>
<DisplayString Condition="((c >> 4) == 0x0d)">D</DisplayString>
<DisplayString Condition="((c >> 4) == 0x0e)">E</DisplayString>
<DisplayString Condition="((c >> 4) == 0x0f)">F</DisplayString>
</Type>
最后的 uuid 可视化工具:
<Type Name="boost::uuids::uuid">
<DisplayString>uuid {*(dummy::hex_dummy_high*)(&data[0])}{*(dummy::hex_dummy_low*)(&data[0])}{*(dummy::hex_dummy_high*)(&data[1])}{*(dummy::hex_dummy_low*)(&data[1])}{*(dummy::hex_dummy_high*)(&data[2])}{*(dummy::hex_dummy_low*)(&data[2])}{*(dummy::hex_dummy_high*)(&data[3])}{*(dummy::hex_dummy_low*)(&data[3])}-{*(dummy::hex_dummy_high*)(&data[4])}{*(dummy::hex_dummy_low*)(&data[4])}{*(dummy::hex_dummy_high*)(&data[5])}{*(dummy::hex_dummy_low*)(&data[5])}-{*(dummy::hex_dummy_high*)(&data[6])}{*(dummy::hex_dummy_low*)(&data[6])}{*(dummy::hex_dummy_high*)(&data[7])}{*(dummy::hex_dummy_low*)(&data[7])}-{*(dummy::hex_dummy_high*)(&data[8])}{*(dummy::hex_dummy_low*)(&data[8])}{*(dummy::hex_dummy_high*)(&data[9])}{*(dummy::hex_dummy_low*)(&data[9])}-{*(dummy::hex_dummy_high*)(&data[10])}{*(dummy::hex_dummy_low*)(&data[10])}{*(dummy::hex_dummy_high*)(&data[11])}{*(dummy::hex_dummy_low*)(&data[11])}{*(dummy::hex_dummy_high*)(&data[12])}{*(dummy::hex_dummy_low*)(&data[12])}{*(dummy::hex_dummy_high*)(&data[13])}{*(dummy::hex_dummy_low*)(&data[13])}{*(dummy::hex_dummy_high*)(&data[14])}{*(dummy::hex_dummy_low*)(&data[14])}{*(dummy::hex_dummy_high*)(&data[15])}{*(dummy::hex_dummy_low*)(&data[15])}</DisplayString>
</Type>
这是一个更紧凑的 ComicSansMS 解决方案版本。我使用 SHA1 结构和可视化工具作为示例。
struct SHA1 { char hash[20]; };
namespace natvis
{
struct x4lo { unsigned __int8 v : 4; unsigned __int8 _ : 4; };
struct x4hi { unsigned __int8 _ : 4; unsigned __int8 v : 4; };
struct x8 { unsigned __int8 _; };
struct x32 { __int32 _; };
}
纳特维斯
<Type Name="natvis::x4hi">
<AlternativeType Name="natvis::x4lo" />
<DisplayString Condition="v==0">0</DisplayString>
<DisplayString Condition="v==1">1</DisplayString>
<DisplayString Condition="v==2">2</DisplayString>
<DisplayString Condition="v==3">3</DisplayString>
<DisplayString Condition="v==4">4</DisplayString>
<DisplayString Condition="v==5">5</DisplayString>
<DisplayString Condition="v==6">6</DisplayString>
<DisplayString Condition="v==7">7</DisplayString>
<DisplayString Condition="v==8">8</DisplayString>
<DisplayString Condition="v==9">9</DisplayString>
<DisplayString Condition="v==10">a</DisplayString>
<DisplayString Condition="v==11">b</DisplayString>
<DisplayString Condition="v==12">c</DisplayString>
<DisplayString Condition="v==13">d</DisplayString>
<DisplayString Condition="v==14">e</DisplayString>
<DisplayString>f</DisplayString>
</Type>
<Type Name="natvis::x8">
<DisplayString>{*(natvis::x4hi*)(this)}{*(natvis::x4lo*)(this)}</DisplayString>
</Type>
<Type Name="natvis::x32">
<DisplayString>{((natvis::x8*)this)[0]}{((natvis::x8*)this)[1]}{((natvis::x8*)this)[2]}{((natvis::x8*)this)[3]}</DisplayString>
</Type>
<Type Name="SHA1">
<DisplayString>{((natvis::x32*)hash)[0]}{((natvis::x32*)hash)[1]}{((natvis::x32*)hash)[2]} {((natvis::x32*)hash)[3]}{((natvis::x32*)hash)[4]}</DisplayString>
</Type>
如果您可以访问代码中定义的字符数组,则可以使用,1sb
字符串格式并避免任何分支。添加[DLL export/extern/static] const char* hex_chars="0123456789abcdef";
到 natvis 命名空间并将 16 个条件 DisplayStrings 替换为一个:
<Type Name="natvis::x4hi">
<AlternativeType Name="natvis::x4lo" />
<DisplayString>{(hex_chars+v),1sb}</DisplayString>
</Type>
据我所知,没有办法以{,,mylib[d].dll}natvis::hex_chars
适用于静态和 DLL 构建的方式使用上下文运算符。您可以使用static const char* hex_chars = "..."
,但这会将字符串添加到包含标题的每个 .obj 文件中。
如果您知道不会导致膨胀的解决方案,请发表评论:)
至少在 VS2019 中不需要虚拟类型或类似的东西。
<Type Name="Guid">
<DisplayString>{{{m_bytes[0],nvoXb}{m_bytes[1],nvoXb}{m_bytes[2],nvoXb}{m_bytes[3],nvoXb}-{m_bytes[4],nvoXb}{m_bytes[5],nvoXb}-{m_bytes[6],nvoXb}{m_bytes[7],nvoXb}-{m_bytes[8],nvoXb}{m_bytes[9],nvoXb}-{m_bytes[10],nvoXb}{m_bytes[11],nvoXb}{m_bytes[12],nvoXb}{m_bytes[13],nvoXb}{m_bytes[14],nvoXb}{m_bytes[15],nvoXb}}}</DisplayString>
</Type>
假设 Guid 类型有一个 m_bytes 成员。其他类型类似,但nvo
仅对于字节/字符值 AFAIK 才是真正需要的。
每个元素访问后的显示参数如下:
nvo
- 仅数值(即字节/字符不包括 ascii 表示)
X
- 大写十六进制显示
b
- 'bare' - 没有前导 0x
,X}
用上面@Xor的答案替换格式说明符,xb}
似乎给出了一个没有VS2019中0x前缀的结果,并且不需要辅助结构。
<Type Name="boost::uuids::uuid">
<DisplayString>{((((uint32_t)data[3] & 0xFF)) + (((uint32_t)data[2] & 0xFF) << 8) + (((uint32_t)data[1] & 0xFF) << 16) + (((uint32_t)data[0] & 0xFF) << 24)),xb} - {((((uint32_t)data[7] & 0xFF)) + (((uint32_t)data[6] & 0xFF) << 8) + (((uint32_t)data[5] & 0xFF) << 16) + (((uint32_t)data[4] & 0xFF) << 24)),xb} - {((((uint32_t)data[11] & 0xFF)) + (((uint32_t)data[10] & 0xFF) << 8) + (((uint32_t)data[9] & 0xFF) << 16) + (((uint32_t)data[8] & 0xFF) << 24)),xb} - {((((uint32_t)data[15] & 0xFF)) + (((uint32_t)data[14] & 0xFF) << 8) + (((uint32_t)data[13] & 0xFF) << 16) + (((uint32_t)data[12] & 0xFF) << 24)),xb}</DisplayString>
</Type>
这是一个简单的解决方案,不需要代码中的任何额外虚拟结构:
<Type Name="boost::uuids::uuid">
<DisplayString>{((((int32)data[3] & 0xFF)) + (((int32)data[2] & 0xFF) << 8) + (((int32)data[1] & 0xFF) << 16) + (((int32)data[0] & 0xFF) << 24)),X} - {((((int32)data[7] & 0xFF)) + (((int32)data[6] & 0xFF) << 8) + (((int32)data[5] & 0xFF) << 16) + (((int32)data[4] & 0xFF) << 24)),X} - {((((int32)data[11] & 0xFF)) + (((int32)data[10] & 0xFF) << 8) + (((int32)data[9] & 0xFF) << 16) + (((int32)data[8] & 0xFF) << 24)),X} - {((((int32)data[15] & 0xFF)) + (((int32)data[14] & 0xFF) << 8) + (((int32)data[13] & 0xFF) << 16) + (((int32)data[12] & 0xFF) << 24)),X}</DisplayString>
</Type>
它没有像其他解决方案那样很好地显示 UUID,它只是将它显示为 4 个 32 位整数块,但它可以完成工作:
你可以试试我的扩展C++ Debugger Visualizers。1.0.16 版支持使用AddIn dll的boost::uuids::uuid 可视化工具。
在搜索并尝试了其他几种解决方案之后,希望可以在不添加虚拟类型的情况下完成此操作,我最终使用了接受的答案。不幸的是,它是为SHA1
类型而不是UUID
类型提供的。在这种情况下,在与“中端”UUID
的 microsoft 交互时,我必须格外小心以正确的顺序输出字节UUID
(即前三组是小端,但后两组是大端)。SMBIOS 规范也遵循中端顺序。
设置与 Johan Torp 建议的相同 - 这必须在您的项目中的某个地方,我将它放在我的UUID
类型旁边:
namespace natvis
{
struct x4lo { unsigned __int8 v : 4; unsigned __int8 _ : 4; };
struct x4hi { unsigned __int8 _ : 4; unsigned __int8 v : 4; };
struct x8 { unsigned __int8 _; };
struct x32 { __int32 _; };
}
在 natvis 文件中:
<Type Name="natvis::x4hi">
<AlternativeType Name="natvis::x4lo" />
<DisplayString Condition="v==0">0</DisplayString>
<DisplayString Condition="v==1">1</DisplayString>
<DisplayString Condition="v==2">2</DisplayString>
<DisplayString Condition="v==3">3</DisplayString>
<DisplayString Condition="v==4">4</DisplayString>
<DisplayString Condition="v==5">5</DisplayString>
<DisplayString Condition="v==6">6</DisplayString>
<DisplayString Condition="v==7">7</DisplayString>
<DisplayString Condition="v==8">8</DisplayString>
<DisplayString Condition="v==9">9</DisplayString>
<DisplayString Condition="v==10">a</DisplayString>
<DisplayString Condition="v==11">b</DisplayString>
<DisplayString Condition="v==12">c</DisplayString>
<DisplayString Condition="v==13">d</DisplayString>
<DisplayString Condition="v==14">e</DisplayString>
<DisplayString>f</DisplayString>
</Type>
<Type Name="natvis::x8">
<DisplayString>{*(natvis::x4hi*)(this)}{*(natvis::x4lo*)(this)}</DisplayString>
</Type>
<Type Name="natvis::x32">
<DisplayString>{((natvis::x8*)this)[0]}{((natvis::x8*)this)[1]}{((natvis::x8*)this)[2]}{((natvis::x8*)this)[3]}</DisplayString>
</Type>
假设您的UUID
类型有一个bytes
成员:
<Type Name="myUUID">
<!-- Assuming system is in little endian, output as middle-endian UUID -->
<DisplayString>{(*(natvis::x32*)&bytes[0])}-{(*(natvis::x8*)&bytes[5])}{(*(natvis::x8*)&bytes[4])}-{(*(natvis::x8*)&bytes[7])}{(*(natvis::x8*)&bytes[6])}-{(*(natvis::x8*)&bytes[8])}{(*(natvis::x8*)&bytes[9])}-{(*(natvis::x8*)&bytes[10])}{(*(natvis::x8*)&bytes[11])}{(*(natvis::x8*)&bytes[12])}{(*(natvis::x8*)&bytes[13])}{(*(natvis::x8*)&bytes[14])}{(*(natvis::x8*)&bytes[15])}</DisplayString>
</Type>
对于正在寻找 QtQUuid
课程的可视化工具的任何人(扩展 Tom Whittock 的回答):
<Type Name="QUuid">
<DisplayString>{{{data1,nvoxb}-{data2,nvoxb}-{data3,nvoxb}-{data4[0],nvoxb}{data4[1],nvoxb}-{data4[2],nvoxb}{data4[3],nvoxb}{data4[4],nvoxb}{data4[5],nvoxb}{data4[6],nvoxb}{data4[7],nvoxb}}}</DisplayString>
<Expand>
<Item Name="[data1]">data1</Item>
<Item Name="[data2]">data2</Item>
<Item Name="[data3]">data3</Item>
<Item Name="[data4]">data4</Item>
</Expand>
</Type>
这将显示带有小写字符的字符串,以便将其与QUuid::toString
.
此可视化工具适用于 Visual Studio 2017。