Ambiguity between class-name and enumerator-values in C++ natvis

174 views Asked by At

I want to create a natvis for QObject. There are dynamic properties concepts, which stored in form

  QList<QByteArray> propertyNames;
  QVector<QVariant> propertyValues;

and I want to group them and visualize as a map(key-value items).

My natvis is pretty simple (for illustration purposes, I replaced all complex logic of getting data from QList and QVector with _GetNameAsVoidPtr and _GetValueAsVoidPtr):

<Type Name="QObject">
  ...
  <Synthetic Name="dynamic_properties">
    <DisplayString>{{ size = {propertyNames.d-&gt;end - propertyNames.d-&gt;begin} }}</DisplayString>
    <Expand>
      <CustomListItems>
        <Variable Name="index" InitialValue="0" />
        <Variable Name="size" InitialValue="propertyNames.d-&gt;end - propertyNames.d-&gt;begin" />
        <Loop>
          <Break Condition="index >= size" />
          <Item Name="[{index}] {*reinterpret_cast&lt;const QByteArray*&gt;(_GetNameAsVoidPtr(index))}">
            {*reinterpret_cast&lt;const QVariant*&gt;(_GetValueAsVoidPtr(index))}
          </Item>
          <Exec>++index</Exec>
        </Loop>
      </CustomListItems>
    </Expand>
  </Synthetic>
  ...
</Type>

I get the following error:

Natvis: QObject.natvis(217,21): Error: constant "QByteArray" is not a type name
    Error while evaluating '*reinterpret_cast<const QByteArray*>(_GetNameAsVoidPtr(index))' in the context of type 'Qt5Cored.dll!QObject'.
Natvis: QObject.natvis(217,21): Error: constant "QVariant" is not a type name
    Error while evaluating '*reinterpret_cast<const QVariant*>(_GetValueAsVoidPtr(index))' in the context of type 'Qt5Widgetsd.dll!QObject'.

I tried to replace reinterpret_cast&lt;const QByteArray*&gt; with reinterpret_cast&lt;const Qt5Cored.dll::QByteArray*&gt;, removed const and other things - nothing worked. Then I printed these values in VS-Watch window and get the following picture: TypesEnums

And here I realised, that Qt has classes: class QByteArray and class QVariant, also, it has enum QMetaType::Type with values QByteArray and QVariant. In most places, natvis use types as-is(as you type them in xml), but for some reason, inside CustomListItems section, it adds the module name in the front of all types.

So, instead of QByteArray and QVariant, it treat them as ModuleName.dll!QByteArray and ModuleName.dll!QVariant. The same for QString, for instance. And as you can see on screenshot from Watch window, Visual studio interpet classes with explicit ModuleName.dll prefix as enum-values(not classes).

It there any way to to specify explicitly that QVariant is a class, not an enum?

I tried to add double colons(::QVariant) or "class" keyword(class QVariant) - does not work :(

I have some ideas how to workaround that issue(if some of them work - i will reply), but first of all, I am curios is there proper way, to tell compiler that it is a class-name?

2

There are 2 answers

1
273K On

If you install Qt Visual Studio Tools add-in, it will automatically install qt5.natvis and qt6.natvis. You can also download qt5.natvis and qt6.natvis, and add them to your project.

As for the error. When your natvis is loaded, QByteArray and QTVariant typs are unknown. You can't use them in expressions.

0
Vladislav On

As a workaround, I used QVector::operator[] for extracting values. Fortunately, this function not optimized and has no side-effects(I am afraid it will not work in Release mode, will test it later).

Tried to do the same for names with QList::operator[] and QList::at(), but get errors:

Natvis: QObject.natvis(222,131): Error: no operator "[]" matches these operands

Error while evaluating 'this->extraData->propertyNames[index]' in the context of type 'Qt5Widgetsd.dll!QObjectPrivate'. Natvis: QObject.natvis(222,131): Error: Function QList::at has no address, possibly due to compiler optimizations.

Error while evaluating 'this->extraData->propertyNames.at(index)' in the context of type 'Qt5Widgetsd.dll!QObjectPrivate'.

So, I decided to copy-paste logic from QByteArray qt5.natvis and convert it to const char*:

<Type Name="QObjectPrivate" Inheritable="true">
  ...
  <Synthetic Name="dynamic_properties">
    <DisplayString Condition="this-&gt;extraData == nullptr">{{ size = 0 }}</DisplayString>
    <DisplayString>{{ size = {this-&gt;extraData-&gt;propertyNames.d-&gt;end - this-&gt;extraData-&gt;propertyNames.d-&gt;begin} }}</DisplayString>
    <Expand>
      <CustomListItems MaxItemsPerView="25">
        <Variable Name="index" InitialValue="0" />
        <Variable Name="size"
                  InitialValue="this-&gt;extraData-&gt;propertyNames.d-&gt;end - this-&gt;extraData-&gt;propertyNames.d-&gt;begin" />
        <Variable Name="p_property_name_byte_array"
                  InitialValue="reinterpret_cast&lt;const char* const*&gt;(this-&gt;extraData-&gt;propertyNames.d-&gt;array)
                                + this-&gt;extraData-&gt;propertyNames.d-&gt;begin" />
        <Loop>
          <Break Condition="index >= size" />
          <Item Name="[{index}] {*p_property_name_byte_array +
                      reinterpret_cast&lt;const QTypedArrayData&lt;char&gt;*&gt;(*p_property_name_byte_array)-&gt;offset,sb}">
            this-&gt;extraData-&gt;propertyValues[index]
          </Item>
          <Exec>++index</Exec>
          <Exec>++p_property_name_byte_array</Exec>
        </Loop>
      </CustomListItems>
    </Expand>
  </Synthetic>
  ...
</Type>

It looks as I expected:

dynamic_properties_visualization

But my question is still relevant. Is there a way to convince visual_studio/natvis that QVariant is a typename, not a QMetaType::Type::QVariant.