6

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458

1 Answers1

8

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

wally
  • 10,717
  • 5
  • 39
  • 72
  • Thanks for the quality answer it explains why it doesn't work. – OeilDeLance Sep 27 '17 at 07:51
  • However using Data Is not a solution because the whole point is to cast the uint8 pointer to a CMyLocalStruct pointer. To make debugging the array easier you know ? – OeilDeLance Sep 27 '17 at 07:54
  • So that it shows up in the watch as an array of CMyLocalStruct – OeilDeLance Sep 27 '17 at 07:55
  • @OeilDeLance Ok, I see that the local type is required to interpret the array correctly. I've posted a workaround, but I don't know if you can update the class. Another, less safe workaround could also be to declare an equivalent dummy struct in your own header file and use that if you cannot change the struct. – wally Sep 27 '17 at 14:30