5

I'm currently writing a visualiser for a date type in natvis. The date type stores seconds since 1970 in the usual unix way but deriving year, month and day of the month from that is extremely long winded if not using temporary variables. I'd like to be able to store working variables in order to evaluate the visualiser in a sane manner. Is this possible?

The solution I arrived at looks like this:

struct SLowResAbsoluteTime
{
    SLowResAbsoluteTime() : mTime(0) { calcDOE(); }
    SLowResAbsoluteTime(int year, SDate::EMonth m, SDate::EDayOfWeek day, UINT8 hour, UINT8 minute, UINT8 seconds);
    SLowResAbsoluteTime(const SDate &date);
    SLowResAbsoluteTime(unsigned long long time) : mTime(time) { calcDOE(); }
    SLowResAbsoluteTime(const SLowResAbsoluteTime &other) : mTime(other.mTime) { calcDOE(); }

    SDate getDate() const; //calculate date object from given time
    UINT32 getHour() const; 
    UINT32 getMinutes() const;
    UINT32 getSeconds() const;

    bool operator < (const SLowResAbsoluteTime &other) const { return mTime < other.mTime; }
    bool operator > (const SLowResAbsoluteTime &other) const { return mTime > other.mTime; }
    bool operator <= (const SLowResAbsoluteTime &other) const { return mTime <= other.mTime; }
    bool operator >= (const SLowResAbsoluteTime &other) const { return mTime >= other.mTime; }
    bool operator == (const SLowResAbsoluteTime &other) const { return mTime == other.mTime; }
    bool operator != (const SLowResAbsoluteTime &other) const { return mTime != other.mTime; }
    SLowResAbsoluteTime operator -(const SLowResAbsoluteTime &time) const { return SLowResAbsoluteTime(mTime - time.mTime); }
    SLowResAbsoluteTime operator +(const SLowResAbsoluteTime &time) const { return SLowResAbsoluteTime(mTime + time.mTime); }
    const SLowResAbsoluteTime &operator -=(const SLowResAbsoluteTime &time) { mTime -= time.mTime; return *this; }
    const SLowResAbsoluteTime &operator +=(const SLowResAbsoluteTime &time) { mTime += time.mTime; return *this; }
    unsigned long long mTime;
    void invalidate() { mTime = -1; }
    bool isValid() const {return mTime != UINT64(-1); }
    operator unsigned long() const { return (long)mTime; }
    void calcDOE();
#ifdef USING_DEBUG_TIMER_DOE
    struct { UINT16 y; UINT8 m; UINT8 d; } mDOE;
#endif
};

Note the 'USING_DEBUG_TIMER_DOE' section. That's calculated like this:

void SLowResAbsoluteTime::calcDOE()
{
#ifdef USING_DEBUG_TIMER_DOE
    int ts = mTime / (60 * 60 * 24);
    int z = ts + 719468;
    int doe = (z - ((z >= 0 ? z : z - 146096) / 146097) * 146097);
    int yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;  // [0, 399]
    int era = (z >= 0 ? z : z - 146096) / 146097;
    int y = (yoe) + era * 400;
    int doy = doe - (365 * yoe + yoe / 4 - yoe / 100);                // [0, 365]
    int mp = (5 * doy + 2) / 153;                                   // [0, 11]
    int d = doy - (153 * mp + 2) / 5 + 1;                             // [1, 31]
    int m = mp + (mp < 10 ? 3 : -9);                            // [1, 12]
    mDOE.y = y + (m <= 2);
    mDOE.m = m;
    mDOE.d = d; 
#endif  
}

and the natvis for visualising these is:

<Type Name="SLowResAbsoluteTime">

    <DisplayString>{{time = { (mTime / (60 * 60)) % 24  }:{(mTime / 60) % 60}:{mTime % 60} } day-1970: {mTime / (60 * 60 * 24)} }</DisplayString>

    <Expand>        
        <Item Name="month">(int)mDOE.m</Item>
        <Item Name="day">(int)mDOE.d</Item>-->
        <Item Name="secs since 1/1/1970"> mTime</Item>  
    </Expand>
</Type>
Luther
  • 1,786
  • 3
  • 21
  • 38
  • Turns out that it's not possible to do this the crazy long handed way due to overflows, not without serious reworking of the maths anyway and that's too much effort for this. I've 'solved' the problem by adding extra variables in the C++ code on the debug build which NatVis can handle but I'd like to know if there's some more powerful tricks in there. A full script language like python or js would be useful. – Luther Apr 19 '20 at 13:38
  • Can you please share a [mcve]/what you have coded so far!? – Werner Henze May 05 '20 at 19:53
  • Sure, I've edited the original post to show what I did – Luther May 06 '20 at 22:32

1 Answers1

7

If you want to eliminate calcDOE and mDOE from your code and do calculation in the proper natvis - yes, it is possible. Use Intrinsic functions:

  <Type Name="SLowResAbsoluteTime">
    <Intrinsic Name="ts"   Expression="mTime / (60 * 60 * 24)                                       "/>
    <Intrinsic Name="z"    Expression="ts() + 719468                                                "/>
    <Intrinsic Name="doe"  Expression="(z() - ((z() &gt;= 0 ? z() : z() - 146096) / 146097) * 146097)"/>
    <Intrinsic Name="yoe"  Expression="(doe() - doe() / 1460 + doe() / 36524 - doe() / 146096) / 365"/>
    <Intrinsic Name="era"  Expression="(z() &gt;= 0 ? z() : z() - 146096) / 146097                  "/>
    <Intrinsic Name="y"    Expression="yoe() + era() * 400                                          "/>
    <Intrinsic Name="doy"  Expression="doe() - (365 * yoe() + yoe() / 4 - yoe() / 100)              "/>
    <Intrinsic Name="mp"   Expression="(5 * doy() + 2) / 153                                        "/>
    <Intrinsic Name="d"    Expression="doy() - (153 * mp() + 2) / 5 + 1                             "/>
    <Intrinsic Name="m"    Expression="mp() + (mp() &lt; 10 ? 3 : -9)                               "/>
    <Intrinsic Name="DOEy" Expression="y() + (m() &lt;= 2)                                          "/>
    <Expand>
      <Item Name="day">d()</Item>
      <Item Name="month">m()</Item>
      <Item Name="year">DOEy()</Item>
    </Expand>
  </Type>
mr NAE
  • 3,144
  • 1
  • 15
  • 35
  • 1
    Ah, look at that, just what I wanted. I wasn't aware of the 'intrinsic' keyword, didn't come across it in the docs I read. – Luther May 07 '20 at 17:50