I am looking for a proper way to display a UUID via NatVis in VS2012
Here's a more compact version of ComicSansMS solution. I'm using a SHA1 struct and visualizer as example instead.
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 _; };
}
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>
<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>
If you can access a character array defined in code you can use the ,1sb
string format and avoid any branching. Add [DLL export/extern/static] const char* hex_chars="0123456789abcdef";
to the natvis namespace and replace the 16 conditional DisplayStrings with a single one:
<Type Name="natvis::x4hi">
<AlternativeType Name="natvis::x4lo" />
<DisplayString>{(hex_chars+v),1sb}</DisplayString>
</Type>
To my knowledge there is no way to use the context operator {,,mylib[d].dll}natvis::hex_chars
in a way that works with both static and DLL builds. You can use static const char* hex_chars = "..."
but that'll add the string to every .obj file that includes the header.
Please leave a comment if you know a solution that doesn't cause bloat :)
There's no need for dummy types or anything like that, at least in 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>
assuming the Guid type has an m_bytes member. Other types are similar, but nvo
is only really necessary for byte/char values AFAIK.
The display parameters following each element access are as follows:
nvo
- numeric value only (i.e. byte/char does not include the ascii representation)
X
- capital hexadecimal display
b
- 'bare' - no leading 0x
This approach is far from pretty but it gets the job done.
First off, you need a dummy type somewhere in your code that handles the display of a single byte in hex without any prefixes. This feels like a really dirty hack, since we have to introduce an additional type into our code just for proper debug visualization.
namespace dummy {
struct hex_dummy {
unsigned char c;
};
}
This type can be placed pretty much anywhere as long as the debugger is able to find it in the context where we want to look at a uuid.
The next step is unfortunately almost as bad. In order to be able to print a byte in hex without the 0x
prefix, we introduce a debug visualizer for hex_dummy
with a whopping 256 different 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>
...
You get the idea.
With that in place, visualizing the uuid is easy. I used boost::uuid
for testing this:
<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>
You can easily verify that it works by testing it with a uuid created by boost's uuid_generator
:
boost::uuids::uuid const test_id =
boost::uuids::string_generator()("{01234567-89AB-CDEF-0123-456789ABCDEF}");
Now this solution is not only abyssmally ugly, it also seems to take the debugger some time to work through the massive hex_dummy
branching, resulting in a noticeable delay for the mouseover watch window to pop up when hovering over a uuid while debugging.
I'm far from happy with this solution, but so far it is the best I could come up with. If anyone sees any potential for improvement without sacrificing the clarity of the final output, I would be really glad to hear them.
Edit: A minor improvement - by introducing two dummy types instead of one I could at least get rid of the popup delay. The idea is two use seperate dummies for printing the upper and lower nibble of each byte, so we have to do two 16-way branches per byte instead of one 256-way branch.
namespace dummy {
struct hex_dummy_low {
unsigned char c;
};
struct hex_dummy_high {
unsigned char c;
};
}
The proxy visualizers:
<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>
And the final uuid visualizer:
<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>
Replacing the format specifier ,X}
with ,xb}
in @Xor's answer above seems to give a result without the 0x prefix in VS2019, and without needing auxiliary structs.
<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>