Search code examples
c++natvis

Function local structs/classes and natvis files


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.


Solution

  • 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