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>