Function local structs/classes and natvis files

Viewed 598

Let's say I have to following struct:

template<class Type, int32 SIZE>
struct TSH2SizedArray
{
    inline void Add(const Type & Value);


    inline Type & operator[](int32 Index);
    inline const Type & operator[](int32 Index)const;

private:
    uint8 Data[SIZE * sizeof(Type)];
    int32 ElemCount = 0;
};


template<class Type, int32 SIZE>
inline void TSH2SizedArray<Type, SIZE>::Add(const Type & Value)
{
    assert(0 <= ElemCount && ElemCount < SIZE);
    *((Type*)(Data + ElemCount++ * sizeof(Type))) = Value;
}

template<class Type, int32 SIZE>
inline Type & TSH2SizedArray<Type, SIZE>::operator[](int32 Index)
{
    assert(0 <= Index && Index < ElemCount);
    return *((Type*)(Data + Index * sizeof(Type)));
}

template<class Type, int32 SIZE>
inline const Type & TSH2SizedArray<Type, SIZE>::operator[](int32 Index)const
{
    assert(0 <= Index && Index < ElemCount);
    return *((Type*)(Data + Index * sizeof(Type)));
}

And the following in my natvis file:

<Type Name="TSH2SizedArray&lt;*,*&gt;">
    <DisplayString>TotalItemCount={ElemCount} (via natvis debug)</DisplayString>
    <Expand>
      <Item Name="TotalItemCount">ElemCount</Item>
      <ArrayItems>
        <Size>ElemCount</Size>
        <ValuePointer>($T1*)Data</ValuePointer>
      </ArrayItems>
    </Expand>
  </Type>

Today I realized that the debug aid provided by the natvis file doesn't work in this situation:

void MyFunc()
{
    struct CMyLocalStruct
    {
        int ValueA;
        int ValueB;
    };
    TSH2SizedArray<CMyLocalStruct, 256> Array;
    Array.Add(CMyLocalStruct(1,2));
}

But works in that one:

// File scope
struct CMyLocalStruct
{
     int ValueA;
     int ValueB;
};
void MyFunc()
{

    TSH2SizedArray<CMyLocalStruct, 256> Array;
    Array.Add(CMyLocalStruct(1,2));
}

If someone has a solution I would be super grateful because that's kind of limiting. But it looks like a bug to me though.

1 Answers

The local struct is a type that is labeled differently by the compiler. So MSVC gives it a name like:

`MyFunc'::`2'::CMyLocalStruct

Natvis looks at the line

($T1*))Data

and replaces the $T1 with the template parameter, which is the local struct in this case and gets:

(`MyFunc'::`2'::CMyLocalStruct*)Data

Finally it complains:

Error: identifier "`MyFunc'" is undefined

Which to me looks like a bug, because it should keep reading the rest of the type, but I'm not sure.


A workaround that I've found is to declare an alias for the template parameter in the struct with a using statement:

template<class Type, int32 SIZE>
struct TSH2SizedArray
{
    inline void Add(const Type & Value);


    inline Type & operator[](int32 Index);
    inline const Type & operator[](int32 Index)const;

    using myType = Type; // natvis will interpret this correctly

private:
    uint8 Data[SIZE * sizeof(Type)];
    int32 ElemCount = 0;
};

And then use the alias:

  <Type Name="TSH2SizedArray&lt;*,*&gt;">
    <DisplayString>TotalItemCount={ElemCount} (via natvis debug)</DisplayString>
    <Expand>
      <Item Name="TotalItemCount">ElemCount</Item>
      <ArrayItems>
        <Size>ElemCount</Size>
        <ValuePointer>(myType*)Data</ValuePointer>
      </ArrayItems>
    </Expand>
  </Type>

Finally natvis does show the correct interpretation for the local type and, ironically, it shows the name of the local type that it could not interpret earlier: natvis showing values

Related